def MiScale(mac_addr, callback, send_only_stabilized_weight): mac_addr = mac_addr.upper() scanner = Scanner().withDelegate(ScanDelegate(mac_addr, callback, send_only_stabilized_weight)) while True: scanner.start() scanner.process(1) scanner.stop()
class DeviceScanner(Scanner): def __init__(self, scanDelegate): Scanner().__init__(self) # self.scanDelegate = ScanDelegate() self.scanner = Scanner().withDelegate(scanDelegate) self.rssi = scanDelegate.rssi self.devices = [] def startScan(self, scanTime=10.0): if not isinf(scanTime): self.devices = self.scanner.scan(scanTime) else: self.scanner.start() while True: print("BLE scan running...") self.scanner.process() def showAvilableDevices(self, scanDelegate): self.rssi = scanDelegate.rssi print("SCANDELEGATE RSSI", scanDelegate.rssi) for dev in self.devices: 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)) print("###########NEXT DEVICE################")
def run(self): scanner = Scanner().withDelegate(PuckClickDelegate()) scanner.start() while not self.stop: print "coucou" scanner.process(10) scanner.stop()
def run(self): while not self.stop_event.wait(2): if not self.scanDelegate.identify_queue.empty(): #check for pending identify command delegate = self.scanDelegate.identify_queue.get() time_wait = .25 cnt = int(delegate.identify_timeout/time_wait) logger.debug('identifying: cnt=' + str(cnt) +', timeout = ' + str(delegate.identify_timeout)) cmd = '{:02x}'.format(tb_protocol.TB_COMMAND_IDENTIFY) for i in range(cnt): f, data = btle_sendcmd(delegate.target_mac, cmd) if f: delegate.identified = True delegate.stop_event.set() #logger.debug('identify succeeded ' + data.hex()) logger.debug('identify succeeded ') break if delegate.stop_event.wait(time_wait): break self.scanDelegate.identify_queue.task_done() scanner = Scanner().withDelegate(self.scanDelegate) try: scanner.clear() scanner.start() scanner.process(10) scanner.stop() except Exception as exc: logger.debug('Exception > ' + str(exc)) pass logger.debug('ScannerThread: exit')
def COLLECTOR(timeout): global runthegame global pmdata global playermonitor global stopmeplease scanner = Scanner().withDelegate(ScanDelegate()) while True: if runthegame: # 0: rawrssi 1:timestamp 2:lastresult 3: ? 4: item counter 5: KalmanRssi 6:KalmanTime pmdata = [ [0, [[], [], 0, 0, 0, [], []]], [1, [[], [], 0, 0, 0, [], []]], [2, [[], [], 0, 0, 0, [], []]], [3, [[], [], 0, 0, 0, [], []]], [4, [[], [], 0, 0, 0, [], []]], ] playermonitor = [] scanner.clear() scanner.start(passive=True) while runthegame: most = timer() scanner.process(0.1) if stopmeplease == True: runthegame = False scanner.stop() time.sleep(0.01)
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
def status(mac_address): print('Looking for switchmate status...') sys.stdout.flush() scanner = Scanner().withDelegate(ScanDelegate(mac_address)) scanner.clear() scanner.start() scanner.process(5) scanner.stop()
def run(self): while True: # Do something print('Starting Scan Process') scanner = Scanner(int(os.environ['SCAN_HCI'])).withDelegate( ScanDelegate()) scanner.clear() scanner.start() scanner.process(5) scanner.stop() time.sleep(self.interval)
def main(): parser = argparse.ArgumentParser( description="Get Xiaomi Mi Smart Scale 2 weight.") parser.add_argument("mac", help="Device MAC address") parser.add_argument('--with-units', action='store_true', help="return weight \w units") parser.add_argument('--verbose', '-v', action='count', default=0) scanner = Scanner().withDelegate(ScanDelegate(parser.parse_args())) while True: scanner.start() scanner.process(2) scanner.stop()
def main(): logging.basicConfig(filename='logs/' + __file__ + '.log', level=logging.INFO, format='%(message)s') scanner = Scanner(BLE_INTERFACE).withDelegate(ScanDelegate()) try: while True: scanner.start() scanner.process(timeout=5) scanner.stop() scanner.clear() except KeyboardInterrupt: pass
def run(self): while True: # Do something print('Starting Scan Process') items = set() scanner = Scanner(int(os.environ['SCAN_HCI'])).withDelegate( ScanDelegate(items)) scanner.clear() scanner.start() scanner.process(5) scanner.stop() for item in items: readnewFirmware(item) time.sleep(self.interval) sys.exit( ) #If the loop exists because of an error, let the process go down and have docker restart it.
def scan(self): """ Scans for PWS advertisements with the given SSID and returns the first match """ logger.info("Start scanning...") scanner = Scanner().withDelegate(self) scanner.clear() scanner.start(passive=True) while self.result is None: scanner.process(0.1) logger.info(f"SSID match in PWS advertisement from {self.result.addr}") scanner.stop() # return and clear result result = self.result self.result = None return result
def main(): bluetooth_adr = sys.argv[1].lower() hci = sys.argv[2] # hci # to use print "Will follow broadcasts from:",bluetooth_adr print "hci used: ",hci while True: try: # BTLE UUSB is on hci0, so pass 0 to Scanner scanner = Scanner(hci).withDelegate(AirMentorProDelegate(bluetooth_adr)) while(1): scanner.start() scanner.process(1) scanner.stop() except: pass
def main(): load_dotenv() if os.path.isfile('refresh_token'): f = open('refresh_token', 'r+') os.environ['refresh_token'] = f.read() parser = argparse.ArgumentParser( description= "Get Xiaomi Mi Smart Scale 2 weight, upload to fitbit, and notify using OpsGenie" ) parser.add_argument('--verbose', '-v', action='count', default=0) scanner = Scanner().withDelegate(scan.ScanDelegate(parser.parse_args())) print("Started Get weight") while True: scanner.start() scanner.process(2) scanner.stop()
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
count = struct.unpack('H', count_bytes)[0] pulse_duration_hex = value[4:12] pulse_duration_bytes = binascii.unhexlify(pulse_duration_hex) pulse_duration = struct.unpack('L', pulse_duration_bytes)[0] battery_voltage_hex = value[12:16] battery_voltage_bytes = binascii.unhexlify(battery_voltage_hex) battery_voltage = unpack('H', battery_voltage_bytes)[0] if (pulse_duration > 0): ticks_per_second = float(1000) / pulse_duration watts = int(round(ticks_per_second * joules_per_tick)) watts_message = short_addr + "watts:" + str(watts) mqtt_client.publish(mqtt_topic, watts_message) count_message = short_addr + "count:" + str(count) mqtt_client.publish(mqtt_topic, count_message) pulse_duration_message = short_addr + "pulseduration:" + str(pulse_duration) mqtt_client.publish(mqtt_topic, pulse_duration_message) battery_message = short_addr + "battery:" + str(battery_voltage) mqtt_client.publish(mqtt_topic, battery_message) scanner = Scanner().withDelegate(ScanDelegate()) scanner.start(passive=True) while True: print "Still running..." scanner.process()
payload["method"] = 'init' else: payload["params"]["knocks"] = knocks payload["params"]["rssi"] = str(rssi) payload["params"]["moduleId"] = str(get_mac()) payload["params"]["braceletId"] = deviceAddr.encode('utf-8') print "Sending datas" + knocks print payload data = json.dumps(payload) requests.post(url, data = data, headers=headers).json() print "data sent" class ScanDelegate(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) def handleDiscovery(self, dev, isNewDev, isNewData): if (dev.addr == "4b:4e:4f:00:00:02" or dev.addr == "4b:4e:4f:00:00:01"): if isNewDev: print "Discovered device", dev.addr knocks = dev.getValueText(33)[-2:].encode('utf-8') deviceAddr = dev.addr scanner.clear() postDatas(deviceAddr, knocks, dev.rssi) scanner = Scanner().withDelegate(ScanDelegate()) scanner.start() scanner.process(0) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
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
print("Discovered relevant device ", dev.addr) print("Station: ", get_mac()) mqclient = mqtt.Client() mqclient.on_connect = on_connect mqclient.on_message = on_message mqclient.connect(broker_hostname, broker_port, 60) results = Results() scanner = Scanner(iface=interface_number).withDelegate(ScanDelegate()) scanner.start(passive=True) mqclient.loop_start() while True: scanner.process(timeout=interval_seconds) devices = scanner.getDevices() results.clear() scanner.clear() for dev in devices: print(f"Device {dev.addr} ({dev.addrType}), RSSI=%{dev.rssi} dB") print("\t ", str(dev.getValue(ScanEntry.COMPLETE_16B_SERVICES))) if device_is_relevant(dev): data = dev.getValue(ScanEntry.SERVICE_DATA_16B) rolling_proximity_id = ''.join('{:02x}'.format(x) for x in data[2:]) # first 2 bytes are the 16 bit service identifier, so the should be 0xfd6f if data[1] != 0xfd and data[0] != 0x6f: # should never happen. invariant violated. print(
class BLE_network: """ BLE network class < UNDER DEVELOPMENT > This class contains of one or multiple peripherals (= device = node = profile). Each of these peripherals can be read from and written to. In addition, if the characteristic supports NOTIFY (notifications), these can also be read. ... attributes ---------- All attributes below are read-only. - netID Network ID methods ------- - scanning scan_for_new_ble_devices(self, device_type, scan_time = 10.0): Scan for new ble devices and asks user to add device to ble network device_type must be of type string scan_time default value is 10.0 seconds __scan_for_ble_devices(self, device_type, scan_time = 10.0): Scan all for all ble devices. It returns a list containing all found ble devices. device_type must be of type string. scan_time default value is 10.0 seconds - peripherals add_peripherals(self, peripheral_addr, addrType = ADDR_TYPE_PUBLIC, iface = None) Add one or mutliple peripherals peripheral_addr must be of type str/unicode (one) or list (one or multiple) addrType and iface must be the same for all peripherals delete_peripherals(self, peripheral_addr = None) Delete all peripherals or specified peripheral(s) with MAC address peripheral_addr peripheral_addr must be of type None (all) str/unicode (one) or list (one or multiple) get_peripherals(self, peripheral_addr = None) Return a list containing the Peripherals of all peripherals or specified peripheral(s) peripheral_addr must be of type None (all), str/unicode (one) or list (one or multiple) - Characteristic control read_characteristics(self, peripheral = None, service_uuid = None, characteristic_uuid = None) Read and save one or multiple characteristics of one or multiple peripherals peripheral must be of type None (all), Peripheral (one) or list (one or multiple) service_uuid and characteristic_uuid must be of type None (all), str/unicode (one) or list (one or multiple) type of return value depends on the characteristic __read_characteristics_for_one_peripheral(self, peripheral, service_uuid = None, characteristic_uuid = None) Read and save characteristics in the memory for one (1) peripheral (currently only printed for debug purposes) peripheral must be of type Peripheral service_uuid and characteristic_uuid must be of type None (all), str/unicode (one) or list (one or multiple) Note: When a service/characteristic is not part of the current peripheral/service, it is ignored write_characteristics(self, value, peripheral = None, service_uuid = None, characteristic_uuid = None) Write 'value' to one or multiple characteristics of one or multiple peripherals value must be of type str peripheral must be of type None (all), Peripheral (one) or list (one or multiple) service_uuid and characteristic_uuid must be of type None (all), str/unicode (one) or list (one or multiple) __write_characteristics_for_one_peripheral(self, peripheral, service_uuid = None, characteristic_uuid = None): Write 'value' to all, one or multiple characteristics of one (1) peripherals (currently with random values for debug purposes) value must be of type str peripheral must be of type Peripheral service_uuid and characteristic_uuid must be of type None (all), str/unicode (one) or list (one or multiple) - Delegates enable_notifications(self, peripheral = None, characteristic_uuid = None, timeout = 2.0, enable = True) Enable/Disable notifications for the specified characteristic(s) on the specified peripheral(s) peripheral must be of type None (all), Peripheral (one) or list (one or multiple) characteristic must be of type None (all) or str (one) timeout has a default value of 2.0 seconds To disable, set enable = False enable_broadcast_data_receive(self, enable = True) Enable/Disable receiving of broadcast data To disable, set enable = False receive_broadcast_data(self, timeout = 10) If new data is availabe, receive broadcast data (must be enabled before) stops when timeout is reached and may be called multiple times when broadcast data is enabled this method must be called continuously if broadcastdata can't be missed NOTE: _DataScanDelegete.handleDiscovery() is called when data is received ------ """ # attributes # @property def netID(self): '''Network ID''' return self.__netID # consturctor # def __init__(self, network_ID, device_type, scan_time = 10.0): '''Initialize a dictionary of peripherals and scan for ble device to add to this network network_ID should not be of type None. Any other type is acceptable device_type must be of type string scan_time default value is 10.0 seconds''' # network ID and empty peripheral dictionary self.__netID = network_ID self.__peripherals = {} # Scan for (new) ble devices self.scan_for_new_ble_devices(device_type, scan_time) # my mqtt init self.__spms_mqtt_client = spms_mqtt_init() # Reset broadcastDataReceiveEnabled self.__broadcastDataReceiveEnabled = False print('') return # destructor def __del__(self): '''Delete network with ID x.''' print('') # Disconnnect from alll peripherals for addr, p in self.__peripherals.iteritems(): print("Disconencting from device with MAC address %s ..." %addr) p.disconnect() print("Disconnecting successfull") # Terminate connection to cloud spms_mqtt_stop(self.__spms_mqtt_client) print("Connection with cloud has been terminated.") # BLE network has been deleted print("BLE network '%s' has been deleted." %self.netID) return # methods # # scanning def scan_for_new_ble_devices(self, device_type, scan_time = 10.0): '''Scan for new ble devices and asks user to add device to ble network device_type must be of type string scan_time default value is 10.0 seconds''' # Scan for ble devices with name = device_type sce_devices = self.__scan_for_ble_devices(device_type, scan_time) # Check if sce_device is empty (no valid devices found) if len(sce_devices) == 0: print("No valid devices found (searched for device type: '%s' (uuid=%s)). Make sure the device is turned on and the correct device_type is searched for." %(device_type, getNameByUUID(device_type))) print('') return # Only add non duplicates to network for sce_device in sce_devices: # Check duplicate if sce_device.addr in self.__peripherals.keys(): # Duplicate print("Duplicate is ignored (MAC address=%s)." %sce_device.addr) else: # Non duplicate response = raw_input("Do you want to connect to device with MAC address %s? [y/N] " % sce_device.addr).lower() if response == "y" or response == "yes": # Accepted connection print("Connecting to device with MAC address %s" % sce_device.addr) self.add_peripherals(sce_device.addr, sce_device.addrType, sce_device.iface) else: # Declined connection print("You did not connect to device '%s' with MAC address %s" %(device_type, sce_device.addr)) continue #skip this device/peripheral print('') return def __scan_for_ble_devices(self, device_type, scan_time = 10.0): '''Scan all for all ble devices. It returns a list containing all found ble devices. device_type must be of type string. scan_time default value is 10.0 seconds.''' # Check if device_type is specified if device_type is None: print("ERROR while trying to scan for ble devices: device_type is not specified. Please try again.") return scanner = Scanner().withDelegate(_DeviceScanDelegate()) sc_devices = scanner.scan(scan_time) # Scan for specified time (in seconds) default = 10.0 seconds # Create empty list of sce_devices (objects of ScanEntry class) sce_devices = [] for device in sc_devices: valid_device = False print("Device %s (%s), RSSI=%d dB" % (device.addr, device.addrType, device.rssi)) # Display all adtype, desc and value of current device for (adtype, desc, value) in device.getScanData(): # adtype (flag), descriptor, value if desc == "Complete Local Name" and value == device_type: print(" %s %s = %s" % (hex(adtype), desc, value)) # normal print # if desc == "Complete 128b Services" and getNameByUUID(value) == device_type: # print(" %s %s = %s (uuid=%s)" % (hex(adtype), desc, getNameByUUID(value), value)) # special print valid_device = True; # Valid device is found (works only for the first device called "Nordic_Blinky" print("Found valid device!") sce_devices.append(device) else: print(" %s %s = %s" % (hex(adtype), desc, value)) # normal print # save + print correct device address if valid_device: print("MAC address valid device: %s\n" % device.addr) else: print("Not a valid device!\n") return sce_devices # peripherals def add_peripherals(self, peripheral_addr, addrType = ADDR_TYPE_PUBLIC, iface = None): '''Add one or mutliple peripherals peripheral_addr must be of type str/unicode (one) or list (one or multiple) addrType and iface must be the same for all peripherals''' # Convert to list if (isinstance(peripheral_addr, str) or isinstance(peripheral_addr, unicode)): peripheral_addr = [peripheral_addr] # Add if (isinstance(peripheral_addr, list)): # Remove duplicates peripheral_addr = list(dict.fromkeys(peripheral_addr)) print("Added peripherals to network with network ID: '%s'" %self.netID) for i in range(len(peripheral_addr)): no_repeat = False no_repeat = self.__peripherals.get(peripheral_addr[i], True) # Create Preripheral object from scanEntry object + Connect to it p_device = Peripheral(peripheral_addr[i], addrType, iface) self.__peripherals[peripheral_addr[i]] = p_device if (no_repeat == True): print(" [new] Added peripheral has MAC address: %s" %peripheral_addr[i]) else: print(" [update] Updated peripheral has MAC address: %s" %peripheral_addr[i]) else: print("ERROR while trying to add peripherals: peripheral is of wrong type (type = %s). Valid types: Peripheral, list (of Peripheral)" %type(peripheral)) print('') return def delete_peripherals(self, peripheral_addr = None): '''Delete all peripherals or specified peripheral(s) with MAC address peripheral_addr peripheral_addr must be of type None (all) str/unicode (one) or list (one or multiple)''' # Convert to list if (isinstance(peripheral_addr, str) or isinstance(peripheral_addr, unicode)): peripheral_addr = [peripheral_addr] # Delete if (peripheral_addr == None): # Disable notifications self.enable_notifications(enable = False) for addr, p in self.__peripherals.iteritems(): print("Disconencting from device with MAC address %s ..." %addr) p.disconnect() print("Successfully disconnected") self.__peripherals.clear() print("Deleted all peripherals from this network") elif (isinstance(peripheral_addr, list)): # Remove duplicates peripheral_addr = list(dict.fromkeys(peripheral_addr)) for i in range(len(peripheral_addr)): p = self.__peripherals.get(peripheral_addr[i], "Not in list") if p != "Not in list": # Disable notifications self.enable_notifications(peripheral = p, enable = False) # Disconnect print("Disconencting from device with MAC address %s ..." %peripheral_addr[i]) self.__peripherals[peripheral_addr[i]].disconnect() print("Disonnecting successfull") # Delete self.__peripherals.pop(peripheral_addr[i]) print("Deleted peripheral with MAC address %s from network" %peripheral_addr[i]) else: print("peripheral with MAC address %s is not part of this network (network ID = %s)" %(peripheral_addr[i], self.netID)) else: print("ERROR while trying to delete peripherals: peripheral is of wrong type (type = %s). Valid types: None, unicode, str or list (of unicode/str)" %type(peripheral_addr)) print('') return def get_peripherals(self, peripheral_addr = None): '''Return a list containing the Peripherals of all peripherals or specified peripheral(s) peripheral_addr must be of type None (all), str/unicode (one) or list (one or multiple)''' p = [] # Convert to list if (isinstance(peripheral_addr, str) or isinstance(peripheral_addr, unicode)): peripheral_addr = [peripheral_adddr] # Get if (peripheral_addr == None): p = self.__peripherals.values() elif (isinstance(peripheral_addr, list)): # Remove duplicates peripheral_addr = list(dict.fromkeys(peripheral_addr)) for i in range(len(peripheral_addr)): val = self.__peripherals.get(peripheral_addr[i], "not in list") if (val == "not in list"): print("peripheral with MAC address '%s' is not part of this network (network ID = %s)" %s(peripheral_addr[i], self.netID)) else: p.append(val) else: print("ERROR while trying to get peripherals: peripheral is of wrong type (type = %s). Valid types: None, unicode, list (of unicode)" %type(peripheral)) return p # Characteristic control def read_characteristics(self, peripheral = None, service_uuid = None, characteristic_uuid = None): '''Read and save one or multiple characteristics of one or multiple peripherals peripheral must be of type None (all), Peripheral (one) or list (one or multiple) service_uuid and characteristic_uuid must be of type None (all), str/unicode (one) or list (one or multiple) type of return value depends on the characteristic''' # Convert to list if peripheral is None: peripheral = self.__peripherals.values() elif isinstance(peripheral, Peripheral): peripheral = [peripheral] # Read if (isinstance(peripheral, list)): # Remove duplicates peripheral = list(dict.fromkeys(peripheral)) for i in range(len(peripheral)): addr = self.__peripherals.get(peripheral[i].addr, "not in list") if (addr == "not in list"): print("peripheral with MAC address %s is not part of this network (network ID = %s)" %(peripheral[i].addr, self.netID)) print("characteristic(s) for this peripheral will not be read") else: # read characteristics self.__read_characteristics_for_one_peripheral(peripheral[i], service_uuid, characteristic_uuid) else: print("ERROR while trying to save characteristics: peripheral is of wrong type (type = %s). Valid type: None, Peripheral or list" %type(peripheral)) print('') return def __read_characteristics_for_one_peripheral(self, peripheral, service_uuid = None, characteristic_uuid = None): '''Read and save characteristics in the memory for one (1) peripheral (currently only printed for debug purposes) peripheral must be of type Peripheral service_uuid and characteristic_uuid must be of type None (all), str/unicode (one) or list (one or multiple) Note: When a service/characteristic is not part of the current peripheral/service, it is ignored''' # Convert to list if service_uuid is None: service_uuid = [] for service in peripheral.getServices(): service_uuid.append(service.uuid) elif isinstance(service_uuid, str) or isinstance(service_uuid, unicode): service_uuid = [service_uuid] elif not isinstance(service_uuid, list): print("ERROR while trying to save characteristics: service_uuid is of wrong type (type = %s). Valid types: None, str, unicode or list" %type(service_uuid)) return # Convert to list if characteristic_uuid is None: characteristic_uuid = [] for characteristic in peripheral.getCharacteristics(): characteristic_uuid.append(characteristic.uuid) elif isinstance(characteristic_uuid, str) or isinstance(characteristic_uuid, unicode): characteristic_uuid = [characteristic_uuid] elif not isinstance(characteristic_uuid, list): print("ERROR while trying to save characteristics: characteristic_uuid is of wrong type (type = %s). Valid types: None, str, unicode or list" %type(characteristic_uuid)) return if not isinstance(peripheral, Peripheral): print("ERROR while trying to save characteristics: peripheral is of wrong type (type = %s). Valid type: Peripheral" %type(peripheral)) return # Remove duplicates service_uuid = list(dict.fromkeys(service_uuid)) characteristic_uuid = list(dict.fromkeys(characteristic_uuid)) # Print device address print("Services and Charactertic uuids of device '%s':" %peripheral.addr) # Get services for se_service in peripheral.getServices(): if (se_service.uuid not in service_uuid): continue # Skip this sevice chars = False # More than 0 characteristics? firstCharacteristic = True # Fisrt characteristic? # Save characteristics for ch_characteristic in se_service.getCharacteristics(): chars = True if (ch_characteristic.uuid not in characteristic_uuid): continue; # Skip this characteristic if firstCharacteristic: # Print current service print("Service:") uuid = se_service.uuid print(getNameByUUID(uuid)) print("(uuid=%s)" %uuid) print(" Characteristics:") firstCharacteristic = False # Print current characteristic uuid = ch_characteristic.uuid characteristic_name = getNameByUUID(uuid) print(" %s" %characteristic_name) print(" (uuid=%s)" %uuid) # Show properties print(" %s" %ch_characteristic.propertiesToString()) # Read characteristic if it supports read if (ch_characteristic.supportsRead() and characteristic_name != "Battery Level"): # Characteristic is readable => Read + print value val = ch_characteristic.read() data = binascii.b2a_hex(val) print(" Current value: %s" % data) if data[-1:] >= "77": # isReady == 0xFF --> is ready to read # Send data to cloud print(" Value: %s" % data) # Only send to cloud when its spms data inDict = spms_ble_names.get(str(uuid), "Not in dict") if inDict is not "Not in dict": print_ble_data(data, "spms", "LITTLE_ENDIAN") temp = float.fromhex(data[8:12])/100 humid = float.fromhex(data[12:16])/100 pressure = float.fromhex(data[16:20]) print(temp, humid, pressure) spms_mqtt_send_data(self.__spms_mqtt_client, temp, humid, pressure) #save_data(peripheral.addr, val) # Print that data is saved print(" Saved! (LBS=%s)" % binascii.b2a_hex(data[-1:])) else: # ignore read value, because isRead = 0x00 (< 0x77) --> is not ready to read print(" Ignored! (LBS=%s)" % binascii.b2a_hex(data[-1:])) else: # Not readable print(" Characteristic '%s' (uuid=%s) is not readble" %(getNameByUUID(ch_characteristic.uuid), ch_characteristic.uuid)) if (chars is False): print(" Service '%s' (uiid=%s) has no characteristics" %(getNameByUUID(se_service.uuid), se_service.uuid)) print('') return def write_characteristics(self, value, peripheral = None, service_uuid = None, characteristic_uuid = None): '''Write 'value' to one or multiple characteristics of one or multiple peripherals value must be of type str peripheral must be of type None (all), Peripheral (one) or list (one or multiple) service_uuid and characteristic_uuid must be of type None (all), str/unicode (one) or list (one or multiple)''' # Check type of value if not isinstance(value, str): print("ERROR while trying to write characteristics: value (%s) is of wrong type (type = %s). Valid type: int or str" %(value, type(value))) print('') return # Convert to list if peripheral is None: peripheral = self.__peripherals.values() elif isinstance(peripheral, Peripheral): peripheral = [peripheral] # Write if (isinstance(peripheral, list)): # Remove duplicates peripheral = list(dict.fromkeys(peripheral)) for i in range(len(peripheral)): addr = self.__peripherals.get(peripheral[i].addr, "not in list") if (addr == "not in list"): print("peripheral with MAC address %s is not part of this network (network ID = %s)" %(peripheral[i].addr, self.netID)) print("characteristic(s) for this peripheral will not be written") else: # write characteristics self.__write_characteristics_for_one_peripheral(value, peripheral[i], service_uuid, characteristic_uuid) else: print("ERROR while trying to write characteristics: peripheral is of wrong type (type = %s). Valid type: None, Peripheral or list" %type(peripheral)) print('') return def __write_characteristics_for_one_peripheral(self, value, peripheral, service_uuid = None, characteristic_uuid = None): '''Write 'value' to all, one or multiple characteristics of one (1) peripherals (currently with random values for debug purposes) value must be of type str peripheral must be of type Peripheral service_uuid and characteristic_uuid must be of type None (all), str/unicode (one) or list (one or multiple)''' # Convert to list if service_uuid is None: service_uuid = [] for service in peripheral.getServices(): service_uuid.append(service.uuid) elif isinstance(service_uuid, str) or isinstance(service_uuid, unicode): service_uuid = [service_uuid] elif not isinstance(service_uuid, list): print("ERROR while trying to write characteristics: service_uuid is of wrong type (type = %s). Valid types: None, str, unicode or list" %type(service_uuid)) return # Convert to list if characteristic_uuid is None: characteristic_uuid = [] for characteristic in peripheral.getCharacteristics(): characteristic_uuid.append(characteristic.uuid) elif isinstance(characteristic_uuid, str) or isinstance(characteristic_uuid, unicode): characteristic_uuid = [characteristic_uuid] elif not isinstance(characteristic_uuid, list): print("ERROR while trying to save characteristics: characteristic_uuid is of wrong type (type = %s). Valid types: None, str, unicode or list" %type(characteristic_uuid)) return if not isinstance(peripheral, Peripheral): print("ERROR while trying to save characteristics: peripheral is of wrong type (type = %s). Valid type: Peripheral" %type(peripheral)) return # Remove duplicates service_uuid = list(dict.fromkeys(service_uuid)) characteristic_uuid = list(dict.fromkeys(characteristic_uuid)) # Print device address print("Services and Charactertic uuids of device '%s':" %peripheral.addr) # Get services for se_service in peripheral.getServices(): if (se_service.uuid not in service_uuid): continue # Skip this sevice chars = False # More than 0 characteristics? firstCharacteristic = True # Fisrt characteristic? # Save characteristics for ch_characteristic in se_service.getCharacteristics(): chars = True if (ch_characteristic.uuid not in characteristic_uuid): continue; # Skip this characteristic if firstCharacteristic: # Print current service print("Service:") uuid = se_service.uuid print(getNameByUUID(uuid)) print("(uuid=%s)" %uuid) print(" Characteristics:") firstCharacteristic = False # Print current characteristic uuid = ch_characteristic.uuid characteristic_name = getNameByUUID(uuid) print(" %s" %characteristic_name) print(" (uuid=%s)" %uuid) # Show properties print(" %s" %ch_characteristic.propertiesToString()) # Write characteristic if it supports writing if (ch_characteristic.properties & ch_characteristic.props["WRITE"]): # Characteristic is readable => Write + print written value ch_characteristic.write(value) print(" Written value: %s" %repr(value)) else: # Not readable print(" Characteristic '%s' (uuid=%s) is not writable" %(getNameByUUID(ch_characteristic.uuid), ch_characteristic.uuid)) return # Delegate def enable_notifications(self, peripheral = None, characteristic_uuid = None, timeout = 2.0, enable = True): '''Enable/Disable notifications for the specified characteristic(s) on the specified peripheral(s) peripheral must be of type None (all), Peripheral (one) or list (one or multiple) characteristic must be of type None (all) or str (one) timeout has a default value of 2.0 seconds To disable, set enable = False''' # Convert to peripheral list if peripheral is None: peripheral = self.__peripherals.values() elif isinstance(peripheral, Peripheral): peripheral = [peripheral] # Handle notifications if (isinstance(peripheral, list)): # Remove duplicates peripheral = list(dict.fromkeys(peripheral)) for i in range(len(peripheral)): addr = self.__peripherals.get(peripheral[i].addr, "not in list") if (addr == "not in list"): print("peripheral with MAC address %s is not part of this network (network ID = %s)" %(peripheral[i].addr, self.netID)) print("Notifications for this peripheral will not be enabled") else: # Convert to characteristic list if (characteristic_uuid is None or isinstance(characteristic_uuid, str)): ch_characteristics = peripheral[i].getCharacteristics(uuid = characteristic_uuid) for j in range(len(ch_characteristics)): if (ch_characteristics[j].properties & ch_characteristics[j].props["NOTIFY"] and ch_characteristics[j].uuid != "00002a19-0000-1000-8000-00805f9b34fb"): handle = ch_characteristics[j].getHandle() currently_disabled = peripheral[i].readCharacteristic(handle + 1) currently_disabled = binascii.b2a_hex(currently_disabled) if enable: if currently_disabled: # with _NotifyDelegate peripheral[i].withDelegate(_NotifyDelegate()) # Enable notifications peripheral[i].writeCharacteristic(handle + 1, b"\x01\x00", True) print("Notifications enabled for characteristic %s on peripheral %s" %(ch_characteristics[j].uuid, peripheral[i].addr)) else: print("Notifications are already enabled for characteristic %s on peripheral %s" %(ch_characteristics[j].uuid, peripheral[i].addr)) else: if currently_disabled: # Disable notifications peripheral[i].writeCharacteristic(handle + 1, b"\x00\x00", True) print("Notifications disabled for characteristic %s on peripheral %s" %(ch_characteristics[j].uuid, peripheral[i].addr)) else: print("Notifications are already disabled for characteristic %s on peripheral %s" %(ch_characteristics[j].uuid, peripheral[i].addr)) else: print("ERROR while trying to enable notifications: characteristic_uuid is of wrong type (type = %s). Valid type: None or str" %type(characteristic_uuid)) else: print("ERROR while trying to enable notifications: peripheral is of wrong type (type = %s). Valid type: None, Peripheral or list" %type(peripheral)) print('') return def enable_broadcast_data_receive(self, enable = True): '''Enable/Disable receiving of broadcast data To disable, set enable = False''' # Enable/Disable if enable == True and self.__broadcastDataReceiveEnabled == False: self.broadcastDataReceiveScanner = Scanner().withDelegate(_DataScanDelegate()) self.broadcastDataReceiveScanner.start() self.__broadcastDataReceiveEnabled = True print("Broadcast data receive is enabled.") elif enable == False and self.__broadcastDataReceiveEnabled == True: self.broadcastDataReceiveScanner.stop() self.__broadcastDataReceiveEnabled = False print("Broadcast data receive is disabled.") print('') return def receive_broadcast_data(self, timeout = 10): '''If new data is availabe, receive broadcast data (must be enabled before) stops when timeout is reached and may be called multiple times when broadcast data is enabled this method must be called continuously if broadcastdata can't be missed NOTE: _DataScanDelegete.handleDiscovery() is called when data is received''' if self.__broadcastDataReceiveEnabled == True: self.broadcastDataReceiveScanner.process(timeout = timeout) print('') print("Checking for new broadcast data...") else: print("ERROR: Braodcast receive is not enabled. Enabled with: BLE_network.enable_broadcast_data_receive(enable = True).") print('') return
class _StoppableScanner(threading.Thread): """Scanner class which can be started and stopped asynchronously. Non-thread-safe. It is implemented as a thread which checks regularly for the stop condition within the :meth:`run()` method; it can be stopped by calling the :meth:`stop()` method. """ def __init__(self, show_warnings=False, *args, **kwargs): """Constructor. Args: show_warnings (bool, optional): If True shows warnings, if any, when discovering devices not respecting the BlueSTSDK's advertising data format, nothing otherwise. """ try: super(_StoppableScanner, self).__init__(*args, **kwargs) self._stop_called = threading.Event() self._process_done = threading.Event() with lock(self): self._scanner = Scanner().withDelegate(_ScannerDelegate(show_warnings)) except BTLEException as e: # Save details of the exception raised but don't re-raise, just # complete the function. import sys self._exc = sys.exc_info() def run(self): """Run the thread.""" self._stop_called.clear() self._process_done.clear() try: with lock(self): self._scanner.clear() self._exc = None self._scanner.start(passive=False) while True: #print('.') self._scanner.process(_ScannerDelegate._SCANNING_TIME_PROCESS_s) if self._stop_called.isSet(): self._process_done.set() break except BTLEException as e: # Save details of the exception raised but don't re-raise, just # complete the function. import sys self._exc = sys.exc_info() def stop(self): """Stop the thread.""" self._stop_called.set() while not (self._process_done.isSet() or self._exc): pass try: self._exc = None with lock(self): self._scanner.stop() except BTLEException as e: # Save details of the exception raised but don't re-raise, just # complete the function. import sys self._exc = sys.exc_info() def join(self): """Join the thread. Raises: :exc:`blue_st_sdk.utils.blue_st_exceptions.BlueSTInvalidOperationException` 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 BlueSTInvalidOperationException(msg)
def scan(self): scanner = Scanner().withDelegate(ScanDelegate()) devices = scanner.clear() devices = scanner.start() data = scanner.process(timeout=10) devices = scanner.stop()
class BleHandler: scanner = None settings = None connectedPeripherals = {} connectedPeripheral = None notificationLoopActive = False notificationResult = None scanningActive = False scanAborted = False subscriptionIds = [] hciIndex = 0 def __init__(self, settings, hciIndex=0): self.connectedPeripherals = {} self.validator = Validator() self.settings = settings self.hciIndex = hciIndex self.scanner = Scanner(self.hciIndex).withDelegate(ScanDelegate()) self.subscriptionIds.append( BluenetEventBus.subscribe(SystemBleTopics.abortScanning, lambda x: self.abortScanning())) def shutDown(self): for subscriptionId in self.subscriptionIds: BluenetEventBus.unsubscribe(subscriptionId) self.validator.shutDown() def connect(self, address): if address not in self.connectedPeripherals: self.connectedPeripherals[address] = Peripheral( iface=self.hciIndex) print("Connecting...") self.connectedPeripheral = address self.connectedPeripherals[address].connect(address, ADDR_TYPE_RANDOM) self.connectedPeripherals[address].getServices() print("Connected") def disconnect(self): print("Disconnecting... Cleaning up") if self.connectedPeripheral: self.connectedPeripherals[self.connectedPeripheral].disconnect() del self.connectedPeripherals[self.connectedPeripheral] self.connectedPeripheral = None print("Cleaned up") def startScanning(self, timeout=3): if not self.scanningActive: self.scanner.start() self.scanningActive = True self.scanAborted = False scanTime = 0 while self.scanningActive and scanTime < timeout and not self.scanAborted: scanTime += 0.5 self.scanner.process(scanTime) self.stopScanning() def startScanningBackground(self, timeout=3): Timer(0.0001, lambda: self.startScanning(timeout)) def stopScanning(self): if self.scanningActive: self.scanner.stop() self.scanningActive = False def abortScanning(self): if self.scanningActive: self.scanAborted = True def enableNotifications(self): print("ENABLE NOTIFICATIONS IS NOT IMPLEMENTED YET") def disableNotifications(self): print("DISABLE NOTIFICATIONS IS NOT IMPLEMENTED YET") def writeToCharacteristic(self, serviceUUID, characteristicUUID, content): targetCharacteristic = self._getCharacteristic(serviceUUID, characteristicUUID) encryptedContent = EncryptionHandler.encrypt(content, self.settings) targetCharacteristic.write(encryptedContent, withResponse=True) def readCharacteristic(self, serviceUUID, characteristicUUID): data = self.readCharacteristicWithoutEncryption( serviceUUID, characteristicUUID) if self.settings.isEncryptionEnabled(): return EncryptionHandler.decrypt(data, self.settings) def readCharacteristicWithoutEncryption(self, serviceUUID, characteristicUUID): targetCharacteristic = self._getCharacteristic(serviceUUID, characteristicUUID) data = targetCharacteristic.read() return data def _getCharacteristic(self, serviceUUID, characteristicUUID): if self.connectedPeripheral: peripheral = self.connectedPeripherals[self.connectedPeripheral] try: service = peripheral.getServiceByUUID(serviceUUID) except BTLEException: raise BluenetBleException( BleError.CAN_NOT_FIND_SERVICE, "Can not find service: " + serviceUUID) characteristics = service.getCharacteristics(characteristicUUID) if len(characteristics) == 0: raise BluenetBleException( BleError.CAN_NOT_FIND_CHACTERISTIC, "Can not find characteristic: " + characteristicUUID) return characteristics[0] else: raise BluenetBleException( BleError.CAN_NOT_GET_CHACTERISTIC, "Can't get characteristic: Not connected.") def setupSingleNotification(self, serviceUUID, characteristicUUID, writeCommand): characteristic = self._getCharacteristic(serviceUUID, characteristicUUID) peripheral = self.connectedPeripherals[self.connectedPeripheral] peripheral.withDelegate( PeripheralDelegate(lambda x: self._killNotificationLoop(x), self.settings)) characteristicCCCDList = characteristic.getDescriptors( forUUID=CCCD_UUID) if len(characteristicCCCDList) == 0: raise BluenetBleException( BleError.CAN_NOT_FIND_CCCD, "Can not find CCCD handle to use notifications for characteristic: " + characteristicUUID) characteristicCCCD = characteristicCCCDList[0] # enable notifications characteristicCCCD.write(b"\x01\x00", True) # execute something that will trigger the notifications writeCommand() self.notificationLoopActive = True loopCount = 0 while self.notificationLoopActive and loopCount < 10: peripheral.waitForNotifications(0.5) loopCount += 1 if self.notificationResult is None: raise BluenetBleException(BleError.NO_NOTIFICATION_DATA_RECEIVED, "No notification data received.") result = self.notificationResult self.notificationResult = None return result def _killNotificationLoop(self, result): self.notificationLoopActive = False self.notificationResult = result
from bluepy.btle import Scanner, DefaultDelegate class ScanDelegate(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) def handleDiscovery(self, dev, isNewDev, isNewData): print "Device %s" % dev.addr for (adtype, desc, value) in dev.getScanData(): print "adtype %s, desc %s, value %s" % (adtype, desc, value) print "---------------" scanner = Scanner().withDelegate(ScanDelegate()) scanner.clear() scanner.start() while True: scanner.process(10) scanner.stop()
def scan(callback, devices=None, raw=False, iface=0, reset_timeout=30, reset_deadtime=300): """Listen for BLE advertising packets from Ruuvitags. Args: callback: Function which will be called for each received Ruuvitag packet. devices: List of BT address strings in format "fe:ed:de:ad:be:ef" for whitelisting devices. If None, no address filtering is made. raw: If False, callback is called with a dictionary with decoded values from advertising message, plus "time" and "rssi" keys which have the packet arrival time and rssi as values. If True, callback is called with a tuple (value, addr, rssi) with the value being the raw BLE packet data, addr the BT address of the sending device and rssi the received signal strength indicator from the BT stack. iface: The number part of the BT interface device (0 for /dev/hci0 etc.) reset_timeout: Time in seconds, after which a reset of the BT adapter is made, if no packets are received. reset_deadtime: Time in seconds. If a BT reset is made and packets are still not arriving, the next reset attempt is made after this time has passed. """ if devices: dev = [s.lower() for s in devices] devices = dev recv_event = threading.Event() reset_thread = threading.Thread(target=idlereset, args=(recv_event, reset_timeout, reset_deadtime)) reset_thread.daemon = True reset_thread.start() # Start scanning scanner = Scanner(iface).withDelegate( ScanDelegate(recv_event, callback, devices, raw)) scanner.clear() scanner.start(passive=True) try: while True: try: scanner.process(0) except BTLEException: try: scanner.stop() except BTLEException: scanner._stopHelper() reset_btadapter() scanner.clear() scanner.start(passive=True) except (KeyboardInterrupt, SystemExit): pass log.info("Stopping scan.") scanner.stop()
for b in activeBeacons: # print "%s, %s, %d, %d, %d, %d, %d, %d" % (b.addr, b.name, len(b.bufferRSSI), b.bufferRSSI[0], b.bufferRSSI[1], b.bufferRSSI[2], b.bufferRSSI[3], b.bufferRSSI[4]) for i in b.bufferRSSI: if abs(i) < distance: powerRelays = True del b.bufferRSSI[0] b.bufferRSSI.append(-999) if not powerRelays: switchRelaysOff() print "Power off" else: print "Power on" scanner.process(scannerTimeOut) # devices = scanner.scan(10.0) # # for dev in devices: # 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) print "Good bye!" except KeyboardInterrupt: print " Quit" GPIO.cleanup()
class BleHandler: def __init__(self, settings, hciIndex=0, scanBackend = ScanBackends.Bluepy): self.connectedPeripherals = {} self.settings = None self.connectedPeripherals = {} self.connectedPeripheral = None self.notificationLoopActive = False self.notificationResult = None self.scanningActive = False self.scanAborted = False self.subscriptionIds = [] self.validator = Validator() self.settings = settings self.hciIndex = hciIndex self.scanBackend = scanBackend if self.scanBackend == ScanBackends.Aio: self.scanner = AioScanner(self.hciIndex).withDelegate(AioScanDelegate(settings)) else: self.scanner = Scanner(self.hciIndex).withDelegate(ScanDelegate(settings)) self.subscriptionIds.append(BluenetEventBus.subscribe(SystemBleTopics.abortScanning, lambda x: self.abortScanning())) def shutDown(self): for subscriptionId in self.subscriptionIds: BluenetEventBus.unsubscribe(subscriptionId) self.validator.shutDown() def connect(self, address): if address not in self.connectedPeripherals: self.connectedPeripherals[address] = Peripheral(iface=self.hciIndex) print("Connecting...") self.connectedPeripheral = address self.connectedPeripherals[address].connect(address, addrType=ADDR_TYPE_RANDOM, iface=self.hciIndex) self.connectedPeripherals[address].getServices() print("Connected") def disconnect(self): print("Disconnecting... Cleaning up") if self.connectedPeripheral: self.connectedPeripherals[self.connectedPeripheral].disconnect() del self.connectedPeripherals[self.connectedPeripheral] self.connectedPeripheral = None print("Cleaned up") def startScanning(self, scanDuration=3): if not self.scanningActive: self.scanningActive = True self.scanAborted = False if self.scanBackend == ScanBackends.Aio: self.scanner.start(scanDuration) else: self.scanner.start() scanTime = 0 processInterval = 0.5 while self.scanningActive and scanTime < scanDuration and not self.scanAborted: scanTime += processInterval self.scanner.process(processInterval) self.stopScanning() def startScanningBackground(self, scanDuration=3): Timer(0.0001, lambda: self.startScanning(scanDuration)) def stopScanning(self): if self.scanningActive: self.scanner.stop() self.scanningActive = False def abortScanning(self): if self.scanningActive: self.scanAborted = True if self.scanBackend == ScanBackends.Aio: self.scanner.stop() def enableNotifications(self): print("ENABLE NOTIFICATIONS IS NOT IMPLEMENTED YET") def disableNotifications(self): print("DISABLE NOTIFICATIONS IS NOT IMPLEMENTED YET") def writeToCharacteristic(self, serviceUUID, characteristicUUID, content): targetCharacteristic = self.getCharacteristic(serviceUUID, characteristicUUID) encryptedContent = EncryptionHandler.encrypt(content, self.settings) targetCharacteristic.write(encryptedContent, withResponse=True) def writeToCharacteristicWithoutEncryption(self, serviceUUID, characteristicUUID, content): byteContent = bytes(content) targetCharacteristic = self.getCharacteristic(serviceUUID, characteristicUUID) targetCharacteristic.write(byteContent, withResponse=True) def readCharacteristic(self, serviceUUID, characteristicUUID): data = self.readCharacteristicWithoutEncryption(serviceUUID, characteristicUUID) if self.settings.isEncryptionEnabled(): return EncryptionHandler.decrypt(data, self.settings) def readCharacteristicWithoutEncryption(self, serviceUUID, characteristicUUID): targetCharacteristic = self.getCharacteristic(serviceUUID, characteristicUUID) data = targetCharacteristic.read() return data def getCharacteristics(self, serviceUUID): if self.connectedPeripheral: peripheral = self.connectedPeripherals[self.connectedPeripheral] try: service = peripheral.getServiceByUUID(serviceUUID) except BTLEException: raise BluenetBleException(BleError.CAN_NOT_FIND_SERVICE, "Can not find service: " + serviceUUID) characteristics = service.getCharacteristics() return characteristics else: raise BluenetBleException(BleError.CAN_NOT_GET_CHACTERISTIC, "Can't get characteristics: Not connected.") def getCharacteristic(self, serviceUUID, characteristicUUID): if self.connectedPeripheral: peripheral = self.connectedPeripherals[self.connectedPeripheral] try: service = peripheral.getServiceByUUID(serviceUUID) except BTLEException: raise BluenetBleException(BleError.CAN_NOT_FIND_SERVICE, "Can not find service: " + serviceUUID) characteristics = service.getCharacteristics(characteristicUUID) if len(characteristics) == 0: raise BluenetBleException(BleError.CAN_NOT_FIND_CHACTERISTIC, "Can not find characteristic: " + characteristicUUID) return characteristics[0] else: raise BluenetBleException(BleError.CAN_NOT_GET_CHACTERISTIC, "Can't get characteristic: Not connected.") def setupSingleNotification(self, serviceUUID, characteristicUUID, writeCommand): characteristic = self.getCharacteristic(serviceUUID, characteristicUUID) peripheral = self.connectedPeripherals[self.connectedPeripheral] peripheral.withDelegate(PeripheralDelegate(lambda x: self._killNotificationLoop(x), self.settings)) characteristicCCCDList = characteristic.getDescriptors(forUUID=CCCD_UUID) if len(characteristicCCCDList) == 0: raise BluenetBleException(BleError.CAN_NOT_FIND_CCCD, "Can not find CCCD handle to use notifications for characteristic: " + characteristicUUID) characteristicCCCD = characteristicCCCDList[0] # enable notifications.. This is ugly but necessary characteristicCCCD.write(b"\x01\x00", True) # execute something that will trigger the notifications writeCommand() self.notificationLoopActive = True loopCount = 0 while self.notificationLoopActive and loopCount < 10: peripheral.waitForNotifications(0.5) loopCount += 1 if self.notificationResult is None: raise BluenetBleException(BleError.NO_NOTIFICATION_DATA_RECEIVED, "No notification data received.") result = self.notificationResult self.notificationResult = None return result def setupNotificationStream(self, serviceUUID, characteristicUUID, writeCommand, resultHandler, timeout): characteristic = self.getCharacteristic(serviceUUID, characteristicUUID) peripheral = self.connectedPeripherals[self.connectedPeripheral] peripheral.withDelegate(PeripheralDelegate(lambda x: self._loadNotificationResult(x), self.settings)) characteristicCCCDList = characteristic.getDescriptors(forUUID=CCCD_UUID) if len(characteristicCCCDList) == 0: raise BluenetBleException(BleError.CAN_NOT_FIND_CCCD, "Can not find CCCD handle to use notifications for characteristic: " + characteristicUUID) characteristicCCCD = characteristicCCCDList[0] # enable notifications.. This is ugly but necessary characteristicCCCD.write(b"\x01\x00", True) # execute something that will trigger the notifications writeCommand() self.notificationLoopActive = True self.notificationResult = None loopCount = 0 successful = False while self.notificationLoopActive and loopCount < timeout*2: peripheral.waitForNotifications(0.5) loopCount += 1 if self.notificationResult is not None: command = resultHandler(self.notificationResult) self.notificationResult = None if command == ProcessType.ABORT_ERROR: self.notificationLoopActive = False raise BluenetBleException(BleError.ABORT_NOTIFICATION_STREAM_W_ERROR, "Aborting the notification stream because the resultHandler raised an error.") elif command == ProcessType.FINISHED: self.notificationLoopActive = False successful = True if not successful: raise BluenetBleException(BleError.NOTIFICATION_STREAM_TIMEOUT, "Notification stream not finished within timeout.") def _killNotificationLoop(self, result): self.notificationLoopActive = False self.notificationResult = result def _loadNotificationResult(self, result): self.notificationResult = result def broadcast(self): pass
def int_from_bytes(xbytes: bytes) -> int: return int.from_bytes(xbytes, 'big') class ScanDelegate(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) def handleDiscovery(self, dev, isNewDev, isNewData): if (any(dev.addr == i for i in device_names)): data = dec.json_i2t(dev.rawData) bundle.update(dev.addr, data) # Update device data in the Bundle Class scanner.clear() scanner.start() ### Start BLE Scanner ### scanner = Scanner().withDelegate(ScanDelegate()) scanner.start() ######################### print("\n ---- RUUVITAG -- IOT2TANGLE ----") print(" Collecting the first data...\n") while True: if (int(time.time()) % interval): scanner.process(1) # Timeout 1 second else: foo()
if len(addr) != 17: raise ValueError("Invalid address supplied") class ScanDelegate(DefaultDelegate): def __init__(self): DefaultDelegate.__init__(self) # when this python script discovers a BLE broadcast packet from a buckler # advertising light measurements, print out the data def handleDiscovery(self, dev, isNewDev, isNewData): if dev.addr == addr: print("found device", dev.addr) data = dev.getValue(ScanEntry.MANUFACTURER) try: # here we are unpacking a little-endian (<) halfword (h) and a # float (f), as well as 20 dummy bytes (20x) values = struct.unpack('<hf20x', data) # if the company ID matches that of Lab11 if values[0] == 0x02e0: print("Value: " + str(values[1]) + " lux") except: print("Got bad packet format") # create a scanner object that sends BLE broadcast packets to the ScanDelegate scanner = Scanner().withDelegate(ScanDelegate()) # start the scanner and keep the process running scanner.start() while True: scanner.process()
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")
complete_name = dev.getValueText(0x09) manufact_data = dev.getValueText(0xff) if complete_name is None or manufact_data is None: return bvalue = bytes.fromhex(manufact_data) if len(bvalue) != 20 or complete_name != 'ThermoBeacon': return data = tb_protocol.TBAdvData(bvalue[0] + (bvalue[1] << 8), bvalue[2:]) #print(manufact_data) print('MAC [{0}], T= {1:5.2f}\xb0C, H = {2:3.2f}%, Button:{4}, Battery : {5:02.0f}%, UpTime = {3:.0f}s'.\ format(dev.addr, data.tmp, data.hum, data.upt, 'On ' if data.btn else 'Off', data.btr)) scanDelegate = ScanDelegate() scanner = Scanner().withDelegate(scanDelegate) while True: try: scanner.clear() scanner.start() scanner.process(20) scanner.stop() except Exception as exc: #print(str(exc)) pass except KeyboardInterrupt: print('\nInterrupted!') sys.exit(0)