def main(): pi.wiringPiSetupGpio() pi.pinMode(BUZZER_GPIOPIN, pi.OUTPUT) service = BeaconService() service.start_advertising() oldbeacon = {} al_notification = {} # hoge = [] devices = service.scan(2) for i, (address, data) in enumerate(list(devices.items())): b = Beacon(data, address) oldbeacon[b.get_uuid()] = b while True: devices = service.scan(1) for i, (address, data) in enumerate(list(devices.items())): b = Beacon(data, address, oldbeacon[data[0]].get_low_rssi(), oldbeacon[data[0]].get_distance()) # hoge.append(b.get_rssi()) print(b) # print(len(hoge)) # if len(hoge) == 100: # print(oldbeacon) # print(sum(hoge)/len(hoge)) # exit(0) is_address, userlist = check_uuid(b.get_uuid()) # send_alert_mails(userlist, b) #出ているのか if is_out_distance(b.get_distance(), oldbeacon[b.get_uuid()].get_distance(), oldbeacon[b.get_uuid()].get_distance( set="old")) and is_address: # 以前通知したかどうか if check_notifincation(b.get_uuid(), al_notification): al_notification[b.get_uuid()] = True nonfication_mails( userlist, "{0}さんが外出しました".format(userlist[0].name), str(datetime.now().strftime("%Y/%m/%d %H:%M:%S")) + "に外出されました") # 入っているのか elif is_in_distance( b.get_distance(), oldbeacon[b.get_uuid()].get_distance(), oldbeacon[b.get_uuid()].get_distance(set="old")): al_notification[b.get_uuid()] = False print("戻った") # 最後に今回のビーコン情報の更新 oldbeacon[b.get_uuid()] = b print("Done.")
def scan_beacon(leave_user): service = BeaconService() devices = service.scan(2) for address, data in list(devices.items()): b = Beacon(data, address) print(b) for i in leave_user: sql_str = "SELECT uuid FROM Employee WHERE Name = "+i+";" cur.execute(sql_str) tmp=cur.fetchall[0][0] if b._uuid==tmp: sql_str = "INSERT INTO time_card(Name,Time,State) VALUES("+i+","+datatime.datatime.now()+","+"enter);" cur.execute(sql_str) print("Done.")
def handle(self, *args, **options): iottalk = dan.Iottalk( dm_name="Dummy_Device", df_list=["Dummy_Sensor", "Dummy_Control"], d_name="Rollcall-Sensor-1", mac=uuid.uuid4().hex, ) iottalk.device_registration_with_retry(os.getenv("IOTTALK_ENDPOINT")) service = BeaconService() try: while True: devices = service.scan(2) for address, data in devices.items(): student = Beacon(data, address) if student._rssi > -40: print("Get UUID: ", student._uuid) iottalk.push("Dummy_Sensor", str(student._uuid)) time.sleep(0.5) finally: iottalk.deregister()
def address(self): return self._address def __str__(self): ret = "Beacon:\n" \ "\taddress: {ADDR}\n" \ "\tuuid: {UUID}\n" \ "\tmajor: {MAJOR} minor: {MINOR} txpower: {POWER}\n" \ "\trssi: {RSSI}\n" \ .format(ADDR=self._address, UUID=self._uuid, MAJOR=self._major, MINOR=self._minor, POWER=self._power, RSSI=self._rssi) return ret counter = 0 serviceB = BeaconService() while True: beacons = serviceB.scan(2) i = 0 os.system('clear') print(counter) print("-"*20) for address, data in list(beacons.items()): b = Beacon(data, address, beacons, i) i += 1 print(b.rssi) print(b.address) print("-"*20) # time.sleep(1) counter += 1
#!/usr/bin/python3 from bluetooth.ble import BeaconService service = BeaconService() devices = service.scan(10) for addr, data in devices.items(): print("%s (UUID %s Major %d Minor %d Power %d RSSI %d)" % (addr, data[0], data[1], data[2], data[3], data[4]))
""" TODO """ def __init__(self, address, data): """ TODO """ self._address = address self._uuid = data[0] self._major = data[1] self._minor = data[2] self._power = data[3] self._rssi = data[4] def __str__(self): """ TODO """ return 'Beacon: address:{ADDR} uuid:{UUID} major:{MAJOR} minor:{MINOR} txpower:{POWER} rssi:{RSSI}'\ .format(ADDR=self._address, UUID=self._uuid, MAJOR=self._major, MINOR=self._minor, POWER=self._power, RSSI=self._rssi) if __name__ == '__main__': # Create beacon service and scan for devices (2 seconds) service = BeaconService('hci0') devices = service.scan(2) # Connect to each beacon, print results of device scan for address, data in list(devices.items()): beacon = Beacon(address, data) print(beacon) print('*** Beacon scan complete! ***')
class Scanner(object): """Instantiates a BLE beacon scanner. Attributes: control_file (pathlib.Path): BLE beacon scanner control file path. timeout (float, int): BLE beacon scanner timeout (s). Must be strictly positive and less than 600. revisit (int): BLE beacon scanner revisit interval (s). Must be strictly positive. filters (dict): Filters to apply to received beacons. Available filters/keys are {'address', 'uuid', 'major', 'minor'}. """ def __init__(self, logger, **kwargs): """Instance initialization. Args: logger (logging.Logger): Configured logger. **kwargs: Keyword arguments corresponding to instance attributes. Any unassociated keyword arguments are ignored. """ # Logger self.__logger = logger # Beacon settings for key, value in DEFAULT_CONFIG['scanner'].items(): if key in kwargs and kwargs[key]: setattr(self, key, kwargs[key]) else: self.__logger.debug("Using default beacon scanner " f"configuration {key}: {value}.") setattr(self, key, value) # Create beacon self.__service = BeaconService(BLE_DEVICE) self.__logger.info("Initialized beacon scanner.") def __del__(self): """Instance destruction.""" if self.__control_file_handle is not None: self.__control_file_handle.close() self.__control_file.unlink() @property def control_file(self): """BLE beacon scanner control file path getter.""" return self.__control_file @control_file.setter def control_file(self, value): """BLE beacon scanner control file path setter. Raises: TypeError: Beacon scanner control file must be a string. """ if not isinstance(value, str): raise TypeError("Beacon scanner control file must be a string.") # Initialize control file self.__control_file = Path(value).resolve() self.__control_file.touch() self.__control_file.chmod(0o777) with self.__control_file.open(mode='w') as f: f.write("0") self.__control_file_handle = None @property def scan_prefix(self): """BLE beacon scanner scan file prefix getter.""" return self.__scan_prefix @scan_prefix.setter def scan_prefix(self, value): """BLE beacon scanner scan file prefix setter. Raises: TypeError: Beacon scanner scan file prefix must be a string. """ if not isinstance(value, str): raise TypeError( "Beacon scanner scan file prefix must be a string.") self.__scan_prefix = value @property def timeout(self): """BLE beacon scanner timeout getter.""" return self.__timeout @timeout.setter def timeout(self, value): """BLE beacon scanner timeout setter. Raises: TypeError: Beacon scanner timeout must be a float, integer, or NoneType. ValueError: Beacon scanner timeout must be strictly positive. ValueError: Beacon scanner cannot exceed maximum allowable timeout. """ if value is not None: if not isinstance(value, (float, int)): raise TypeError("Beacon scanner timeout must be a float, " "integer, or NoneType.") elif value <= 0: raise ValueError("Beacon scanner timeout must be strictly " "positive.") elif value > MAX_TIMEOUT: raise ValueError("Beacon scanner timeout cannot exceed " "maximum allowable timeout.") self.__timeout = value @property def revisit(self): """BLE beacon scanner revisit interval getter.""" return self.__revisit @revisit.setter def revisit(self, value): """BLE beacon scanner revisit interval setter. Raises: TypeError: Beacon scanner revisit interval must be an integer. ValueError: Beacon scanner revisit interval must be strictly positive. """ if not isinstance(value, int): raise TypeError("Beacon scanner revisit interval must be an " "integer.") elif value <= 0: raise ValueError("Beacon scanner revisit interval must strictly " "positive.") self.__revisit = value @property def filters(self): """BLE beacon scanner filters getter.""" return self.__filters @filters.setter def filters(self, value): """BLE beacon scanner filters setter. Raises: TypeError: Beacon scanner filters must be a dictionary. KeyError: Beacon scanner filters must be one of allowable filters. """ if not isinstance(value, dict): raise TypeError("Beacon scanner filters must be a dictionary.") elif not all([key in ALLOWABLE_FILTERS for key in value.keys()]): raise KeyError("Beacon scanner filters must be one of allowable " f"filters {ALLOWABLE_FILTERS}.") self.__filters = value def filter_advertisements(self, advertisements): """Filter received beacon advertisements based on filters. Args: advertisements (pandas.DataFrame): Parsed advertisements. Returns: Advertisements with all entries that were not compliant with the filters removed. """ for key, value in self.filters.items(): # Filter based on fixed identifiers if key in ID_FILTERS: advertisements = advertisements[advertisements[key].isin( [value])] # Filter based on measurements else: query_str = f"{value[0]} <= {key} and {key} <= {value[1]}" advertisements = advertisements.query(query_str) advertisements.reset_index(inplace=True, drop=True) return advertisements def process_scans(self, scans, timestamps): """Process collection of received beacon advertisement scans. Organize collection of received beacon advertisement scans according to address, payload, and measurements. Args: scans (list): Received beacon advertisement scans. Each element contains all advertisements received from one scan. Elements are in temporal order. timestamps (list): Timestamps associated with each scan. Returns: Advertisements organized in a pandas.DataFrame by address first, timestamp second, and then remainder of advertisement payload, e.g., UUID, major, minor, etc. """ # Collect all advertisements advertisements = [] for (scan, timestamp) in zip_longest(scans, timestamps): for address, payload in scan.items(): advertisement = {'ADDRESS': address, 'TIMESTAMP': timestamp} advertisement['UUID'] = payload[0] advertisement['MAJOR'] = payload[1] advertisement['MINOR'] = payload[2] advertisement['TX POWER'] = payload[3] advertisement['RSSI'] = payload[4] advertisements.append(advertisement) # Format into DataFrame return pd.DataFrame(advertisements, columns=[ 'ADDRESS', 'TIMESTAMP', 'UUID', 'MAJOR', 'MINOR', 'TX POWER', 'RSSI' ]) def nameScanLogs(self): latestNum = self.curr_file_id for file in os.listdir("~/reference_code"): if file.endswith(".csv"): print(os.path.join("", file)) return None def scan(self, scan_prefix='', timeout=0, revisit=1, curr_file_id=0): """Execute BLE beacon scan. Args: scan_prefix (str): Scan output file prefix. Final output file name will be appended with first scan start timestamp. Defaults to configuration value. timeout (int, float): Time (s) for which to advertise beacon. If specified as None then advertises till user commanded stop via control file. Defaults to configuration value. revisit (int): Time interval (s) between consecutive scans. Defaults to 1. Returns: Filtered advertisements organized in a pandas.DataFrame by address first, timestamp second, and then remainder of advertisement payload, e.g., UUID, major, minor, etc. """ # Parse inputs if scan_prefix == '': scan_prefix = self.scan_prefix if timeout == 0: timeout = self.timeout # Update control file and scan output file with open(self.__control_file, 'w') as f: f.write("0") latestNum = self.curr_file_id for file in os.listdir("pact_scans"): if file.endswith(".csv"): currNum = int( re.findall('\d+', str(os.path.join("", file)))[0]) if (currNum >= latestNum): latestNum = currNum + 1 scan_file = Path(f"pact_scans/scan_{latestNum}.csv") # scan_file = Path(f"{scan_prefix}_{datetime.now():%Y%m%dT%H%M%S}.csv") # Start advertising self.__logger.info(f"Starting beacon scanner with timeout {timeout}.") self.__control_file_handle = self.__control_file.open(mode='r+') run = True timestamps = [] scans = [] scan_count = 0 start_time = time.monotonic() while run: scan_count += 1 self.__logger.debug(f"Performing scan #{scan_count} at revisit " f"{self.revisit}.") timestamps.append(datetime.now()) scans.append(self.__service.scan(self.revisit)) # Stop advertising based on either timeout or control file if timeout is not None: if (time.monotonic() - start_time) > timeout: self.__logger.debug("Beacon scanner timed out.") run = False self.__control_file_handle.seek(0) control_flag = self.__control_file_handle.read() if control_flag != "0": self.__logger.debug("Beacon scanner control flag set to stop.") run = False self.__logger.info("Stopping beacon scanner.") # Cleanup self.__control_file_handle.close() with self.__control_file.open('w') as f: f.write("0") # Process, filter, and output received scans advertisements = self.process_scans(scans, timestamps) advertisements = self.filter_advertisements(advertisements) advertisements.to_csv(scan_file, index_label='SCAN') return advertisements
from bluetooth.ble import BeaconService class Beacon(object): def __init__(self, data, address): self._uuid = data[0] self._major = data[1] self._minor = data[2] self._power = data[3] self._rssi = data[4] self._address = address def __str__(self): ret = "Beacon: address:{ADDR} uuid:{UUID} major:{MAJOR}"\ " minor:{MINOR} txpower:{POWER} rssi:{RSSI}"\ .format(ADDR=self._address, UUID=self._uuid, MAJOR=self._major, MINOR=self._minor, POWER=self._power, RSSI=self._rssi) return ret service = BeaconService() devices = service.scan(2) for address, data in list(devices.items()): b = Beacon(data, address) print(b) print("Done.")
service = BeaconService() # Start the service object as beacon service def getMaxRssi(): rssiM = -100 beacon = Beacon.Beacon([0, 0, 0, 0, -100], "") for beaconRead in beaconsRead: if int(beaconRead._rssi) > rssiM: rssiM = beaconRead._rssi beacon = beaconRead return beacon while True: beaconsRead = [] devices = service.scan(DISCOVER_TIME) # Scan the devices inside the beacon service for address, data in list( devices.items()): # Run for loop for the scanned beacons b = Beacon.Beacon(data, address) # Create the object b from class Beacon beaconsRead.append(b) for beacon in beaconsList: if addressM != getMaxRssi()._address and int(getMaxRssi( )._rssi) >= -90 and beacon["BeaconMAC"] == getMaxRssi()._address: addressM = getMaxRssi()._address dataI = {} dataI["name"] = beacon["Station"] dataI["date"] = time.time() print(getMaxRssi()._address)
class Scanner(object): def __init__(self, **kwargs): """Instance initialization. Args: logger (logging.Logger): Configured logger. **kwargs: Keyword arguments corresponding to instance attributes. Any unassociated keyword arguments are ignored. """ # Beacon settings for key, value in DEFAULT_CONFIG['scanner'].items(): if key in kwargs and kwargs[key]: setattr(self, key, kwargs[key]) # Create beacon self.__service = BeaconService(BLE_DEVICE) self.__timeout = None @property def timeout(self): """BLE beacon scanner timeout getter.""" return self.__timeout @timeout.setter def timeout(self, value): """BLE beacon scanner timeout setter. Raises: TypeError: Beacon scanner timeout must be a float, integer, or NoneType. ValueError: Beacon scanner timeout must be strictly positive. ValueError: Beacon scanner cannot exceed maximum allowable timeout. """ if value is not None: if not isinstance(value, (float, int)): raise TypeError("Beacon scanner timeout must be a float, " "integer, or NoneType.") elif value <= 0: raise ValueError("Beacon scanner timeout must be strictly " "positive.") elif value > MAX_TIMEOUT: raise ValueError("Beacon scanner timeout cannot exceed " "maximum allowable timeout.") self.__timeout = value @property def revisit(self): """BLE beacon scanner revisit interval getter.""" return self.__revisit @revisit.setter def revisit(self, value): """BLE beacon scanner revisit interval setter. Raises: TypeError: Beacon scanner revisit interval must be an integer. ValueError: Beacon scanner revisit interval must be strictly positive. """ if not isinstance(value, int): raise TypeError("Beacon scanner revisit interval must be an " "integer.") elif value <= 0: raise ValueError("Beacon scanner revisit interval must strictly " "positive.") self.__revisit = value def scan(self, scan_prefix='', timeout=0, revisit=1): """Execute BLE beacon scan. Returns: Filtered advertisements by address first, timestamp second, and then remainder of advertisement payload, e.g., UUID, major, minor, etc. """ # Parse inputs if timeout == 0: timeout = self.timeout # Start advertising run = True scan_count = 0 start_time = time.monotonic() advertisements = [] while run: scan_count += 1 scans = [] scans.append(self.__service.scan(self.revisit)) print(scans[-1]) if timeout is not None: if (time.monotonic() - start_time) > timeout: run = False
from bluetooth.ble import BeaconService import dbFunctions import Beacon import time DISCOVER_TIME = 3 # In seconds, scan interval duration. service = BeaconService() # Start the service object as beacon service dbFunctions.createTable() # If not exist, create the table sensors while True: devices = service.scan(DISCOVER_TIME) # Scan the devices inside the beacon service for address, data in list(devices.items()): # Run for loop for the scanned beacons b = Beacon.Beacon(data, address) # Create the object b from class Beacon print(b) sensor = {} # Initialize the dictonary sensor sensor["date"] = time.time() # Add current time sensor["address"] = b._address # Add beacon address sensor["rssi"] = b._rssi # Add beacon signal level rssi dbFunctions.insertSensorEvent(sensor) # Insert sensor event to database
class Beacon(object): def __init__(self, data, address): self._uuid = data[0] self._major = data[1] self._minor = data[2] self._power = data[3] self._rssi = data[4] self._address = address def __str__(self): return '{0}, RSSI: {1}'.format(self._address, self._rssi) datadir = '/home/pi/data' beacon_service = BeaconService() while True: devices = beacon_service.scan(30) if devices: with open('/home/pi/data/data.txt', 'a+') as f: for address, data in devices.items(): b = Beacon(data, address) print(b) dt = datetime.now() f.write('{0},{1},{2},{3},{4},{5},{6},{7}\n'.format(b._address, b._rssi, dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second)) else: print('no devices')
class Scanner(object): """Instantiates a BLE beacon scanner. Attributes: timeout_s (float, int): BLE beacon scanner timeout (s). Must be strictly positive and less than 600. revisit (int): BLE beacon scanner revisit interval (s). Must be strictly positive. filters (dict): Filters to apply to received beacons. Available filters/keys are {'address', 'uuid', 'major', 'minor'}. """ def __init__(self, logger, **kwargs): """Instance initialization. Args: logger (logging.Logger): Configured logger. **kwargs: Keyword arguments corresponding to instance attributes. Any unassociated keyword arguments are ignored. """ # Logger self.__logger = logger # Beacon settings for key, value in DEFAULT_CONFIG['scanner'].items(): if key in kwargs and kwargs[key]: setattr(self, key, kwargs[key]) else: self.__logger.debug("Using default beacon scanner " f"configuration {key}: {value}.") setattr(self, key, value) # Create beacon self.__service = BeaconService(BLE_DEVICE) # GPIO.output(6,GPIO.HIGH); # self.__logger.info("Initialized beacon scanner.") def __del__(self): """Instance destruction.""" # GPIO.output(6,GPIO.LOW); @property def scan_prefix(self): """BLE beacon scanner scan file prefix getter.""" return self.__scan_prefix @scan_prefix.setter def scan_prefix(self, value): """BLE beacon scanner scan file prefix setter. Raises: TypeError: Beacon scanner scan file prefix must be a string. """ if not isinstance(value, str): raise TypeError( "Beacon scanner scan file prefix must be a string.") self.__scan_prefix = value @property def timeout_s(self): """BLE beacon scanner timeout getter.""" return self.__timeout_s @timeout_s.setter def timeout_s(self, value): """BLE beacon scanner timeout setter. Raises: TypeError: Beacon scanner timeout must be a float, integer, or NoneType. ValueError: Beacon scanner timeout must be strictly positive. ValueError: Beacon scanner cannot exceed maximum allowable timeout. """ if value is not None: if not isinstance(value, (float, int)): raise TypeError("Beacon scanner timeout must be a float, " "integer, or NoneType.") elif value <= 0: raise ValueError("Beacon scanner timeout must be strictly " "positive.") elif value > MAX_TIMEOUT: raise ValueError("Beacon scanner timeout cannot exceed " "maximum allowable timeout.") self.__timeout_s = value @property def revisit(self): """BLE beacon scanner revisit interval getter.""" return self.__revisit @revisit.setter def revisit(self, value): """BLE beacon scanner revisit interval setter. Raises: TypeError: Beacon scanner revisit interval must be an integer. ValueError: Beacon scanner revisit interval must be strictly positive. """ if not isinstance(value, int): raise TypeError("Beacon scanner revisit interval must be an " "integer.") elif value <= 0: raise ValueError("Beacon scanner revisit interval must strictly " "positive.") self.__revisit = value @property def filters(self): """BLE beacon scanner filters getter.""" return self.__filters @filters.setter def filters(self, value): """BLE beacon scanner filters setter. Raises: TypeError: Beacon scanner filters must be a dictionary. KeyError: Beacon scanner filters must be one of allowable filters. """ if not isinstance(value, dict): raise TypeError("Beacon scanner filters must be a dictionary.") elif not all([key in ALLOWABLE_FILTERS for key in value.keys()]): raise KeyError("Beacon scanner filters must be one of allowable " f"filters {ALLOWABLE_FILTERS}.") self.__filters = value def filter_advertisements(self, advertisements): """Filter received beacon advertisements based on filters. Args: advertisements (pandas.DataFrame): Parsed advertisements. Returns: Advertisements with all entries that were not compliant with the filters removed. """ for key, value in self.filters.items(): # Filter based on fixed identifiers if key in ID_FILTERS: advertisements = advertisements[advertisements[key].isin( [value])] # Filter based on measurements else: query_str = f"{value[0]} <= {key} and {key} <= {value[1]}" advertisements = advertisements.query(query_str) advertisements.reset_index(inplace=True, drop=True) return advertisements def process_scans(self, scans, timestamps): """Process collection of received beacon advertisement scans. Organize collection of received beacon advertisement scans according to address, payload, and measurements. Args: scans (list): Received beacon advertisement scans. Each element contains all advertisements received from one scan. Elements are in temporal order. timestamps (list): Timestamps associated with each scan. Returns: Advertisements organized in a pandas.DataFrame by address first, timestamp second, and then remainder of advertisement payload, e.g., UUID, major, minor, etc. """ # Collect all advertisements advertisements = [] for (scan, timestamp) in zip_longest(scans, timestamps): for address, payload in scan.items(): advertisement = { 'ADDRESS': address, 'TIMESTAMP': timestamp.replace(microsecond=0) } advertisement['UUID'] = payload[0] advertisement['MAJOR'] = payload[1] advertisement['MINOR'] = payload[2] advertisement['TX_POWER'] = payload[3] if payload[3] < ( TX_POWER_LIMITS[1] + 1) else -(256 - payload[3]) * 2 advertisement['RSSI'] = payload[4] if int(advertisement['RSSI']) < int(advertisement['TX_POWER']): advertisement['SOS'] = 0 else: # close range detected !!! GPIO.output(26, GPIO.HIGH) advertisement['SOS'] = 1 advertisements.append(advertisement) # Format into DataFrame return pd.DataFrame(advertisements, columns=[ 'TIMESTAMP', 'UUID', 'ADDRESS', 'MAJOR', 'MINOR', 'TX_POWER', 'RSSI', 'SOS' ]) def scan(self, scan_prefix='', timeout_s=5, revisit=0): """Execute BLE beacon scan. Args: scan_prefix (str): Scan output file prefix. Final output file name will be appended with first scan start timestamp. Defaults to configuration value. timeout_s (int, float): Time (s) for which to scan for beacons. Defaults to configuration value. revisit (int): Time interval (s) between consecutive scans. Defaults to 1. Returns: Filtered advertisements organized in a pandas.DataFrame by address first, timestamp second, and then remainder of advertisement payload, e.g., UUID, major, minor, etc. """ # Parse inputs if scan_prefix == '': scan_prefix = self.scan_prefix if timeout_s == 0: timeout_s = self.timeout_s # scan_file = Path(f"{scan_prefix}_{datetime.now():%Y%m%dT%H%M%S}.csv") scan_file = Path(f"{scan_prefix}.csv") # Start scanning # self.__logger.info(f"Starting beacon scanner with timeout {timeout_s}.") ## GPIO.output(6,GPIO.HIGH); run = True timestamps = [] scans = [] scan_count = 0 start_time = time.monotonic() while run: scan_count += 1 # self.__logger.debug(f"Performing scan #{scan_count} at revisit " # f"{self.revisit}.") timestamps.append(datetime.now()) scans.append(self.__service.scan(self.revisit)) # Stop scanning based on timeout if timeout_s is not None: if (time.monotonic() - start_time) > timeout_s: # self.__logger.debug("Beacon scanner timed out.") run = False # self.__logger.info("Stopping beacon scanner.") ## GPIO.output(6,GPIO.LOW); # Cleanup # Process, filter, and output received scans advertisements = self.process_scans(scans, timestamps) advertisements = self.filter_advertisements(advertisements) # if file does not exist write header if not os.path.isfile(scan_file): advertisements.to_csv(scan_file, index=False, index_label=False) else: advertisements.to_csv(scan_file, mode='a', index=False, index_label=False, header=False) return advertisements
class Scanner(object): """Instantiates a BLE beacon scanner. Attributes: control_file (pathlib.Path): BLE beacon scanner control file path. timeout (float, int): BLE beacon scanner timeout (s). Must be strictly positive and less than 600. revisit (int): BLE beacon scanner revisit interval (s). Must be strictly positive. filters (dict): Filters to apply to received beacons. Available filters/keys are {'address', 'uuid', 'major', 'minor'}. """ def __init__(self, logger, **kwargs): """Instance initialization. Args: logger (logging.Logger): Configured logger. **kwargs: Keyword arguments corresponding to instance attributes. Any unassociated keyword arguments are ignored. """ # Logger self.__logger = logger # Beacon settings for key, value in DEFAULT_CONFIG['scanner'].items(): if key in kwargs and kwargs[key]: setattr(self, key, kwargs[key]) else: self.__logger.debug("Using default beacon scanner " f"configuration {key}: {value}.") setattr(self, key, value) # Create beacon self.__service = BeaconService(BLE_DEVICE) self.__logger.info("Initialized beacon scanner.") def __del__(self): """Instance destruction.""" if self.__control_file_handle is not None: self.__control_file_handle.close() self.__control_file.unlink() @property def control_file(self): """BLE beacon scanner control file path getter.""" return self.__control_file @control_file.setter def control_file(self, value): """BLE beacon scanner control file path setter. Raises: TypeError: Beacon scanner control file must be a string. """ if not isinstance(value, str): raise TypeError("Beacon scanner control file must be a string.") # Initialize control file self.__control_file = Path(value).resolve() self.__control_file.touch() self.__control_file.chmod(0o777) with self.__control_file.open(mode='w') as f: f.write("0") self.__control_file_handle = None @property def scan_prefix(self): """BLE beacon scanner scan file prefix getter.""" return self.__scan_prefix @scan_prefix.setter def scan_prefix(self, value): """BLE beacon scanner scan file prefix setter. Raises: TypeError: Beacon scanner scan file prefix must be a string. """ if not isinstance(value, str): raise TypeError( "Beacon scanner scan file prefix must be a string.") self.__scan_prefix = value @property def timeout(self): """BLE beacon scanner timeout getter.""" return self.__timeout @timeout.setter def timeout(self, value): """BLE beacon scanner timeout setter. Raises: TypeError: Beacon scanner timeout must be a float, integer, or NoneType. ValueError: Beacon scanner timeout must be strictly positive. ValueError: Beacon scanner cannot exceed maximum allowable timeout. """ if value is not None: if not isinstance(value, (float, int)): raise TypeError("Beacon scanner timeout must be a float, " "integer, or NoneType.") elif value <= 0: raise ValueError("Beacon scanner timeout must be strictly " "positive.") elif value > MAX_TIMEOUT: raise ValueError("Beacon scanner timeout cannot exceed " "maximum allowable timeout.") self.__timeout = value @property def revisit(self): """BLE beacon scanner revisit interval getter.""" return self.__revisit @revisit.setter def revisit(self, value): """BLE beacon scanner revisit interval setter. Raises: TypeError: Beacon scanner revisit interval must be an integer. ValueError: Beacon scanner revisit interval must be strictly positive. """ if not isinstance(value, (int)): raise TypeError("Beacon scanner revisit interval must be an " "integer") elif value <= 0: raise ValueError("Beacon scanner revisit interval must strictly " "positive.") self.__revisit = value @property def filters(self): """BLE beacon scanner filters getter.""" return self.__filters @filters.setter def filters(self, value): """BLE beacon scanner filters setter. Raises: TypeError: Beacon scanner filters must be a dictionary. KeyError: Beacon scanner filters must be one of allowable filters. """ if not isinstance(value, dict): raise TypeError("Beacon scanner filters must be a dictionary.") elif not all([key in ALLOWABLE_FILTERS for key in value.keys()]): raise KeyError("Beacon scanner filters must be one of allowable " f"filters {ALLOWABLE_FILTERS}.") self.__filters = value def filter_advertisements(self, advertisements): """Filter received beacon advertisements based on filters. Args: advertisements (pandas.DataFrame): Parsed advertisements. Returns: Advertisements with all entries that were not compliant with the filters removed. """ for key, value in self.filters.items(): # Filter based on fixed identifiers if key in ID_FILTERS: advertisements = advertisements[advertisements[key].isin( [value])] # Filter based on measurements else: query_str = f"{value[0]} <= {key} and {key} <= {value[1]}" advertisements = advertisements.query(query_str) advertisements.reset_index(inplace=True, drop=True) return advertisements def process_scans(self, scans, timestamps): """Process collection of received beacon advertisement scans. Organize collection of received beacon advertisement scans according to address, payload, and measurements. Args: scans (list): Received beacon advertisement scans. Each element contains all advertisements received from one scan. Elements are in temporal order. timestamps (list): Timestamps associated with each scan. Returns: Advertisements organized in a pandas.DataFrame by address first, timestamp second, and then remainder of advertisement payload, e.g., UUID, major, minor, etc. """ # Collect all advertisements advertisements = [] for bucket_id in scans.keys(): cnt = 0 for (scan, timestamp) in zip_longest(scans[bucket_id], timestamps[bucket_id]): advertisement = pd.Series(np.zeros(10), index=[ 'RSSI', 'ax', 'ay', 'az', 'gx', 'gy', 'gz', 'mx', 'my', 'mz' ]) for address, payload in scan[0].items(): advertisement['RSSI'] += payload[4] cnt += 1 advertisement['ax'] += scan[1][0] advertisement['ay'] += scan[1][1] advertisement['az'] += scan[1][2] advertisement['gx'] += scan[2][0] advertisement['gy'] += scan[2][1] advertisement['gz'] += scan[2][2] advertisement['mx'] += scan[3][0] advertisement['my'] += scan[3][1] advertisement['mz'] += scan[3][2] advertisement / cnt advertisement['ADDRESS'] = address advertisement['TIMESTAMP'] = timestamp advertisement['UUID'] = payload[0] advertisement['MAJOR'] = payload[1] advertisement['MINOR'] = payload[2] advertisement['TX POWER'] = payload[3] advertisements.append(advertisement) print(advertisement) # Format into DataFrame return pd.DataFrame(advertisements, columns=[ 'ADDRESS', 'TIMESTAMP', 'UUID', 'MAJOR', 'MINOR', 'TX POWER', 'RSSI', 'ax', 'ay', 'az', 'gx', 'gy', 'gz', 'mx', 'my', 'mz' ]) def scan(self, scan_prefix='', timeout=0, revisit=1): """Execute BLE beacon scan. Args: scan_prefix (str): Scan output file prefix. Final output file name will be appended with first scan start timestamp. Defaults to configuration value. timeout (int, float): Time (s) for which to advertise beacon. If specified as None then advertises till user commanded stop via control file. Defaults to configuration value. revisit (int): Time interval (s) between consecutive scans. Defaults to 1. Returns: Filtered advertisements organized in a pandas.DataFrame by address first, timestamp second, and then remainder of advertisement payload, e.g., UUID, major, minor, etc. """ #Printing Test bucket_start_time = datetime.now() print(bucket_start_time) # Parse inputs if scan_prefix == '': scan_prefix = self.scan_prefix if timeout == 0: timeout = self.timeout # Update control file and scan output file with open(self.__control_file, 'w') as f: f.write("0") scan_file = Path(f"{scan_prefix}_{datetime.now():%Y%m%dT%H%M%S}.csv") # Start advertising self.__logger.info(f"Starting beacon scanner with timeout {timeout}.") self.__control_file_handle = self.__control_file.open(mode='r+') run = True timestamps = defaultdict(list) scans = defaultdict(list) scan_count = 0 current_bucket_id = 0 #9250 mpu = MPU9250( address_ak=AK8963_ADDRESS, address_mpu_master=MPU9050_ADDRESS_68, # In 0x68 Address address_mpu_slave=None, bus=1, gfs=GFS_1000, afs=AFS_8G, mfs=AK8963_BIT_16, mode=AK8963_MODE_C100HZ) mpu.configure() mpu.calibrate() print("Done Calibration") mpu.configure() start_time = time.monotonic() while run: scan_count += 1 self.__logger.debug(f"Performing scan #{scan_count} at revisit " f"{self.revisit}.") ble_scan = self.__service.scan(self.revisit) diff = datetime.now() - bucket_start_time bucket_id = diff.seconds // TIME_INTERVAL if ble_scan: accel = mpu.readAccelerometerMaster() gyro = mpu.readMagnetometerMaster() mag = mpu.readGyroscopeMaster() if current_bucket_id != bucket_id: print("New bucket created") current_bucket_id = bucket_id scans[current_bucket_id].append([ble_scan, accel, gyro, mag]) timestamps[current_bucket_id].append(datetime.now()) # Stop advertising based on either timeout or control file if timeout is not None: if (time.monotonic() - start_time) > timeout: self.__logger.debug("Beacon scanner timed out.") run = False self.__control_file_handle.seek(0) control_flag = self.__control_file_handle.read() if control_flag != "0": self.__logger.debug("Beacon scanner control flag set to stop.") run = False self.__logger.info("Stopping beacon scanner.") # Cleanup self.__control_file_handle.close() with self.__control_file.open('w') as f: f.write("0") # Process, filter, and output received scans advertisements = self.process_scans(scans, timestamps) advertisements = self.filter_advertisements(advertisements) advertisements.to_csv(scan_file, index_label='SCAN') return advertisements
def beacon_scan(): service = BeaconService() return service.scan(2)