516 lines
20 KiB
Python
Executable File
516 lines
20 KiB
Python
Executable File
#!/usr/bin/python3 -u
|
|
#!/home/openhabian/Python3/Python-3.7.4/python -u
|
|
#-u to unbuffer output. Otherwise when calling with nohup or redirecting output things are printed very lately or would even mixup
|
|
|
|
from bluepy import btle
|
|
import argparse
|
|
import os
|
|
import re
|
|
from dataclasses import dataclass
|
|
from collections import deque
|
|
import threading
|
|
import time
|
|
import signal
|
|
import traceback
|
|
import math
|
|
import logging
|
|
|
|
@dataclass
|
|
class Measurement:
|
|
temperature: float
|
|
humidity: int
|
|
voltage: float
|
|
calibratedHumidity: int = 0
|
|
battery: int = 0
|
|
timestamp: int = 0
|
|
sensorname: str = ""
|
|
rssi: int = 0
|
|
|
|
def __eq__(self, other): #rssi may be different
|
|
if self.temperature == other.temperature and self.humidity == other.humidity and self.calibratedHumidity == other.calibratedHumidity and self.battery == other.battery and self.voltage == other.voltage and self.sensorname == other.sensorname:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
measurements=deque()
|
|
#globalBatteryLevel=0
|
|
previousMeasurement=Measurement(0,0,0,0,0,0,0,0)
|
|
identicalCounter=0
|
|
|
|
def signal_handler(sig, frame):
|
|
if args.atc:
|
|
disable_le_scan(sock)
|
|
os._exit(0)
|
|
|
|
def watchDog_Thread():
|
|
global unconnectedTime
|
|
global connected
|
|
global pid
|
|
while True:
|
|
logging.debug("watchdog_Thread")
|
|
logging.debug("unconnectedTime : " + str(unconnectedTime))
|
|
logging.debug("connected : " + str(connected))
|
|
logging.debug("pid : " + str(pid))
|
|
now = int(time.time())
|
|
if (unconnectedTime is not None) and ((now - unconnectedTime) > 60): #could also check connected is False, but this is more fault proof
|
|
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
|
|
logging.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
|
|
logging.debug("Couldn't find pid of bluepy-helper")
|
|
os.system("kill " + bluepypid)
|
|
logging.debug("Killed bluepy with pid: " + str(bluepypid))
|
|
unconnectedTime = now #reset unconnectedTime to prevent multiple killings in a row
|
|
time.sleep(5)
|
|
|
|
|
|
def thread_SendingData():
|
|
global previousMeasurement
|
|
global measurements
|
|
path = os.path.dirname(os.path.abspath(__file__))
|
|
while True:
|
|
try:
|
|
mea = measurements.popleft()
|
|
if (mea == previousMeasurement and identicalCounter < args.skipidentical): #only send data when it has changed or X identical data has been skipped, ~10 pakets per minute, 50 pakets --> writing at least every 5 minutes
|
|
print("Measurements are identical don't send data\n")
|
|
identicalCounter+=1
|
|
continue
|
|
identicalCounter=0
|
|
fmt = "sensorname,temperature,humidity,voltage" #don't try to seperate by semicolon ';' os.system will use that as command seperator
|
|
if ' ' in mea.sensorname:
|
|
sensorname = '"' + mea.sensorname + '"'
|
|
else:
|
|
sensorname = mea.sensorname
|
|
params = sensorname + " " + str(mea.temperature) + " " + str(mea.humidity) + " " + str(mea.voltage)
|
|
if (args.TwoPointCalibration or args.offset): #would be more efficient to generate fmt only once
|
|
fmt +=",humidityCalibrated"
|
|
params += " " + str(mea.calibratedHumidity)
|
|
if (args.battery):
|
|
fmt +=",batteryLevel"
|
|
params += " " + str(mea.battery)
|
|
if (args.rssi):
|
|
fmt +=",rssi"
|
|
params += " " + str(mea.rssi)
|
|
params += " " + str(mea.timestamp)
|
|
fmt +=",timestamp"
|
|
cmd = path + "/" + args.callback + " " + fmt + " " + params
|
|
print(cmd)
|
|
ret = os.system(cmd)
|
|
if (ret != 0):
|
|
measurements.appendleft(mea) #put the measurement back
|
|
print ("Data couln't be send to Callback, retrying...")
|
|
time.sleep(5) #wait before trying again
|
|
else: #data was sent
|
|
previousMeasurement=Measurement(mea.temperature,mea.humidity,mea.voltage,mea.calibratedHumidity,mea.battery,0) #using copy or deepcopy requires implementation in the class definition
|
|
|
|
except IndexError:
|
|
#print("Keine Daten")
|
|
time.sleep(1)
|
|
except Exception as e:
|
|
print(e)
|
|
print(traceback.format_exc())
|
|
|
|
sock = None #from ATC
|
|
lastBLEPaketReceived = 0
|
|
BLERestartCounter = 1
|
|
def keepingLEScanRunning(): #LE-Scanning gets disabled sometimes, especially if you have a lot of BLE connections, this thread periodically enables BLE scanning again
|
|
global BLERestartCounter
|
|
while True:
|
|
time.sleep(1)
|
|
now = time.time()
|
|
if now - lastBLEPaketReceived > args.watchdogtimer:
|
|
print("Watchdog: Did not receive any BLE Paket within", int(now - lastBLEPaketReceived), "s. Restarting BLE scan. Count:", BLERestartCounter)
|
|
disable_le_scan(sock)
|
|
enable_le_scan(sock, filter_duplicates=False)
|
|
BLERestartCounter += 1
|
|
print("")
|
|
time.sleep(5) #give some time to take effect
|
|
|
|
|
|
def calibrateHumidity2Points(humidity, offset1, offset2, calpoint1, calpoint2):
|
|
#offset1=args.offset1
|
|
#offset2=args.offset2
|
|
#p1y=args.calpoint1
|
|
#p2y=args.calpoint2
|
|
p1y=calpoint1
|
|
p2y=calpoint2
|
|
p1x=p1y - offset1
|
|
p2x=p2y - offset2
|
|
m = (p1y - p2y) * 1.0 / (p1x - p2x) # y=mx+b
|
|
#b = (p1x * p2y - p2x * p1y) * 1.0 / (p1y - p2y)
|
|
b = p2y - m * p2x #would be more efficient to do this calculations only once
|
|
humidityCalibrated=m*humidity + b
|
|
if (humidityCalibrated > 100 ): #with correct calibration this should not happen
|
|
humidityCalibrated = 100
|
|
elif (humidityCalibrated < 0):
|
|
humidityCalibrated = 0
|
|
humidityCalibrated=int(round(humidityCalibrated,0))
|
|
return humidityCalibrated
|
|
|
|
|
|
mode="round"
|
|
class MyDelegate(btle.DefaultDelegate):
|
|
def __init__(self, params):
|
|
btle.DefaultDelegate.__init__(self)
|
|
# ... initialise here
|
|
|
|
def handleNotification(self, cHandle, data):
|
|
global measurements
|
|
try:
|
|
measurement = Measurement(0,0,0,0,0,0,0,0)
|
|
if args.influxdb == 1:
|
|
measurement.timestamp = int((time.time() // 10) * 10)
|
|
else:
|
|
measurement.timestamp = int(time.time())
|
|
temp=int.from_bytes(data[0:2],byteorder='little',signed=True)/100
|
|
#print("Temp received: " + str(temp))
|
|
if args.round:
|
|
#print("Temperatur unrounded: " + str(temp
|
|
|
|
if args.debounce:
|
|
global mode
|
|
temp*=10
|
|
intpart = math.floor(temp)
|
|
fracpart = round(temp - intpart,1)
|
|
#print("Fracpart: " + str(fracpart))
|
|
if fracpart >= 0.7:
|
|
mode="ceil"
|
|
elif fracpart <= 0.2: #either 0.8 and 0.3 or 0.7 and 0.2 for best even distribution
|
|
mode="trunc"
|
|
#print("Modus: " + mode)
|
|
if mode=="trunc": #only a few times
|
|
temp=math.trunc(temp)
|
|
elif mode=="ceil":
|
|
temp=math.ceil(temp)
|
|
else:
|
|
temp=round(temp,0)
|
|
temp /=10.
|
|
#print("Debounced temp: " + str(temp))
|
|
else:
|
|
temp=round(temp,1)
|
|
humidity=int.from_bytes(data[2:3],byteorder='little')
|
|
print("Temperature: " + str(temp))
|
|
print("Humidity: " + str(humidity))
|
|
voltage=int.from_bytes(data[3:5],byteorder='little') / 1000.
|
|
print("Battery voltage:",voltage,"V")
|
|
measurement.temperature = temp
|
|
measurement.humidity = humidity
|
|
measurement.voltage = voltage
|
|
measurement.sensorname = args.name
|
|
if args.battery:
|
|
#measurement.battery = globalBatteryLevel
|
|
batteryLevel = min(int(round((voltage - 2.1),2) * 100), 100) #3.1 or above --> 100% 2.1 --> 0 %
|
|
measurement.battery = batteryLevel
|
|
print("Battery level:",batteryLevel)
|
|
|
|
|
|
if args.offset:
|
|
humidityCalibrated = humidity + args.offset
|
|
print("Calibrated humidity: " + str(humidityCalibrated))
|
|
measurement.calibratedHumidity = humidityCalibrated
|
|
|
|
if args.TwoPointCalibration:
|
|
humidityCalibrated= calibrateHumidity2Points(humidity,args.offset1,args.offset2, args.calpoint1, args.calpoint2)
|
|
print("Calibrated humidity: " + str(humidityCalibrated))
|
|
measurement.calibratedHumidity = humidityCalibrated
|
|
|
|
if(args.callback):
|
|
measurements.append(measurement)
|
|
|
|
|
|
except Exception as e:
|
|
print("Fehler")
|
|
print(e)
|
|
print(traceback.format_exc())
|
|
|
|
# Initialisation -------
|
|
|
|
def connect():
|
|
#print("Interface: " + str(args.interface))
|
|
p = btle.Peripheral(adress,iface=args.interface)
|
|
val=b'\x01\x00'
|
|
p.writeCharacteristic(0x0038,val,True) #enable notifications of Temperature, Humidity and Battery voltage
|
|
p.writeCharacteristic(0x0046,b'\xf4\x01\x00',True)
|
|
p.withDelegate(MyDelegate("abc"))
|
|
return p
|
|
|
|
# Main loop --------
|
|
parser=argparse.ArgumentParser(allow_abbrev=False)
|
|
parser.add_argument("--device","-d", help="Set the device MAC-Address in format AA:BB:CC:DD:EE:FF",metavar='AA:BB:CC:DD:EE:FF')
|
|
parser.add_argument("--battery","-b", help="Get estimated battery level", metavar='', type=int, nargs='?', const=1)
|
|
parser.add_argument("--count","-c", help="Read/Receive N measurements and then exit script", metavar='N', type=int)
|
|
parser.add_argument("--interface","-i", help="Specifiy the interface number to use, e.g. 1 for hci1", metavar='N', type=int, default=0)
|
|
parser.add_argument("--unreachable-count","-urc", help="Exit after N unsuccessful connection tries", metavar='N', type=int, default=0)
|
|
|
|
|
|
rounding = parser.add_argument_group("Rounding and debouncing")
|
|
rounding.add_argument("--round","-r", help="Round temperature to one decimal place",action='store_true')
|
|
rounding.add_argument("--debounce","-deb", help="Enable this option to get more stable temperature values, requires -r option",action='store_true')
|
|
|
|
offsetgroup = parser.add_argument_group("Offset calibration mode")
|
|
offsetgroup.add_argument("--offset","-o", help="Enter an offset to the reported humidity value",type=int)
|
|
|
|
complexCalibrationGroup=parser.add_argument_group("2 Point Calibration")
|
|
complexCalibrationGroup.add_argument("--TwoPointCalibration","-2p", help="Use complex calibration mode. All arguments below are required",action='store_true')
|
|
complexCalibrationGroup.add_argument("--calpoint1","-p1", help="Enter the first calibration point",type=int)
|
|
complexCalibrationGroup.add_argument("--offset1","-o1", help="Enter the offset for the first calibration point",type=int)
|
|
complexCalibrationGroup.add_argument("--calpoint2","-p2", help="Enter the second calibration point",type=int)
|
|
complexCalibrationGroup.add_argument("--offset2","-o2", help="Enter the offset for the second calibration point",type=int)
|
|
|
|
callbackgroup = parser.add_argument_group("Callback related arguments")
|
|
callbackgroup.add_argument("--callback","-call", help="Pass the path to a program/script that will be called on each new measurement")
|
|
callbackgroup.add_argument("--name","-n", help="Give this sensor a name reported to the callback script")
|
|
callbackgroup.add_argument("--skipidentical","-skip", help="N consecutive identical measurements won't be reported to callbackfunction",metavar='N', type=int, default=0)
|
|
callbackgroup.add_argument("--influxdb","-infl", help="Optimize for writing data to influxdb,1 timestamp optimization, 2 integer optimization",metavar='N', type=int, default=0)
|
|
|
|
atcgroup = parser.add_argument_group("ATC mode related arguments")
|
|
atcgroup.add_argument("--atc","-a", help="Read the data of devices with custom ATC firmware flashed",action='store_true')
|
|
atcgroup.add_argument("--watchdogtimer","-wdt",metavar='X', type=int, help="Re-enable scanning after not receiving any BLE packet after X seconds")
|
|
atcgroup.add_argument("--devicelistfile","-df",help="Specify a device list file giving further details to devices")
|
|
atcgroup.add_argument("--onlydevicelist","-odl", help="Only read devices which are in the device list file",action='store_true')
|
|
atcgroup.add_argument("--rssi","-rs", help="Report RSSI via callback",action='store_true')
|
|
|
|
|
|
args=parser.parse_args()
|
|
if args.device:
|
|
if re.match("[0-9a-fA-F]{2}([:]?)[0-9a-fA-F]{2}(\\1[0-9a-fA-F]{2}){4}$",args.device):
|
|
adress=args.device
|
|
else:
|
|
print("Please specify device MAC-Address in format AA:BB:CC:DD:EE:FF")
|
|
os._exit(1)
|
|
elif not args.atc:
|
|
parser.print_help()
|
|
os._exit(1)
|
|
|
|
if args.TwoPointCalibration:
|
|
if(not(args.calpoint1 and args.offset1 and args.calpoint2 and args.offset2)):
|
|
print("In 2 Point calibration you have to enter 4 points")
|
|
os._exit(1)
|
|
elif(args.offset):
|
|
print("Offset calibration and 2 Point calibration can't be used together")
|
|
os._exit(1)
|
|
if not args.name:
|
|
args.name = args.device
|
|
|
|
if args.callback:
|
|
dataThread = threading.Thread(target=thread_SendingData)
|
|
dataThread.start()
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
if args.device:
|
|
|
|
p=btle.Peripheral()
|
|
cnt=0
|
|
|
|
|
|
connected=False
|
|
#logging.basicConfig(level=logging.DEBUG)
|
|
logging.basicConfig(level=logging.ERROR)
|
|
logging.debug("Debug: Starting script...")
|
|
pid=os.getpid()
|
|
bluepypid=None
|
|
unconnectedTime=None
|
|
connectionLostCounter=0
|
|
|
|
watchdogThread = threading.Thread(target=watchDog_Thread)
|
|
watchdogThread.start()
|
|
logging.debug("watchdogThread started")
|
|
|
|
while True:
|
|
try:
|
|
if not connected:
|
|
#Bluepy sometimes hangs and makes it even impossible to connect with gatttool as long it is running
|
|
#on every new connection a new bluepy-helper is called
|
|
#we now make sure that the old one is really terminated. Even if it hangs a simple kill signal was sufficient to terminate it
|
|
# if bluepypid is not None:
|
|
# os.system("kill " + bluepypid)
|
|
# print("Killed possibly remaining bluepy-helper")
|
|
# else:
|
|
# print("bluepy-helper couldn't be determined, killing not allowed")
|
|
|
|
print("Trying to connect to " + adress)
|
|
p=connect()
|
|
# logging.debug("Own PID: " + str(pid))
|
|
# 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
|
|
# logging.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
|
|
# logging.debug("Couldn't find pid of bluepy-helper")
|
|
connected=True
|
|
unconnectedTime=None
|
|
|
|
# if args.battery:
|
|
# if(cnt % args.battery == 0):
|
|
# print("Warning the battery option is deprecated, Aqara device always reports 99 % battery")
|
|
# batt=p.readCharacteristic(0x001b)
|
|
# batt=int.from_bytes(batt,byteorder="little")
|
|
# print("Battery-Level: " + str(batt))
|
|
# globalBatteryLevel = batt
|
|
|
|
|
|
if p.waitForNotifications(2000):
|
|
# handleNotification() was called
|
|
|
|
cnt += 1
|
|
if args.count is not None and cnt >= args.count:
|
|
print(str(args.count) + " measurements collected. Exiting in a moment.")
|
|
p.disconnect()
|
|
time.sleep(5)
|
|
#It seems that sometimes bluepy-helper remains and thus prevents a reconnection, so we try killing our own bluepy-helper
|
|
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
|
|
bluepypid=0
|
|
try:
|
|
bluepypid=re.findall(r'bluepy-helper\((.*)\)',pstree)[0] #Store the bluepypid, to kill it later
|
|
except IndexError: #Should normally occur because we're disconnected
|
|
logging.debug("Couldn't find pid of bluepy-helper")
|
|
if bluepypid != 0:
|
|
os.system("kill " + bluepypid)
|
|
logging.debug("Killed bluepy with pid: " + str(bluepypid))
|
|
os._exit(0)
|
|
print("")
|
|
continue
|
|
except Exception as e:
|
|
print("Connection lost")
|
|
connectionLostCounter +=1
|
|
if connected is True: #First connection abort after connected
|
|
unconnectedTime=int(time.time())
|
|
connected=False
|
|
if args.unreachable_count != 0 and connectionLostCounter >= args.unreachable_count:
|
|
print("Maximum numbers of unsuccessful connections reaches, exiting")
|
|
os._exit(0)
|
|
time.sleep(1)
|
|
logging.debug(e)
|
|
logging.debug(traceback.format_exc())
|
|
|
|
print ("Waiting...")
|
|
# Perhaps do something else here
|
|
|
|
elif args.atc:
|
|
print("Script started in ATC Mode")
|
|
print("----------------------------")
|
|
print("In this mode all devices within reach are read out, unless a namefile and --namefileonlydevices is specified.")
|
|
print("Also --name Argument is ignored, if you require names, please use --namefile.")
|
|
print("In this mode rounding and debouncing are not available, since ATC firmware sends out only one decimal place.")
|
|
print("ATC mode usually requires root rights. If you want to use it with normal user rights, \nplease execute \"sudo setcap cap_net_raw,cap_net_admin+eip $(eval readlink -f `which python3`)\"")
|
|
print("You have to redo this step if you upgrade your python version.")
|
|
print("----------------------------")
|
|
|
|
import sys
|
|
import bluetooth._bluetooth as bluez
|
|
|
|
from bluetooth_utils import (toggle_device,
|
|
enable_le_scan, parse_le_advertising_events,
|
|
disable_le_scan, raw_packet_to_str)
|
|
|
|
advCounter=dict()
|
|
sensors = dict()
|
|
if args.devicelistfile:
|
|
import configparser
|
|
if not os.path.exists(args.devicelistfile):
|
|
print ("Error specified device list file '",args.devicelistfile,"' not found")
|
|
os._exit(1)
|
|
sensors = configparser.ConfigParser()
|
|
sensors.read(args.devicelistfile)
|
|
|
|
if args.onlydevicelist and not args.devicelistfile:
|
|
print("Error: --onlydevicelist requires --devicelistfile <devicelistfile>")
|
|
os._exit(1)
|
|
|
|
dev_id = args.interface # the bluetooth device is hci0
|
|
toggle_device(dev_id, True)
|
|
|
|
try:
|
|
sock = bluez.hci_open_dev(dev_id)
|
|
except:
|
|
print("Cannot open bluetooth device %i" % dev_id)
|
|
raise
|
|
|
|
enable_le_scan(sock, filter_duplicates=False)
|
|
|
|
try:
|
|
prev_data = None
|
|
|
|
def le_advertise_packet_handler(mac, adv_type, data, rssi):
|
|
global lastBLEPaketReceived
|
|
if args.watchdogtimer:
|
|
lastBLEPaketReceived = time.time()
|
|
lastBLEPaketReceived = time.time()
|
|
#print("reveived BLE packet")
|
|
data_str = raw_packet_to_str(data)
|
|
ATCPaketMAC = data_str[10:22].upper()
|
|
macStr = mac.replace(":","").upper()
|
|
atcIdentifier = data_str[6:10].upper()
|
|
if(atcIdentifier == "1A18" and ATCPaketMAC == macStr) and not args.onlydevicelist or (atcIdentifier == "1A18" and mac in sensors): #only Data from ATC devices, double checked
|
|
advNumber = data_str[-2:]
|
|
if macStr in advCounter:
|
|
lastAdvNumber = advCounter[macStr]
|
|
else:
|
|
lastAdvNumber = None
|
|
if lastAdvNumber == None or lastAdvNumber != advNumber:
|
|
advCounter[macStr] = advNumber
|
|
print("BLE packet: %s %02x %s %d" % (mac, adv_type, data_str, rssi))
|
|
#print("AdvNumber: ", advNumber)
|
|
#temp = data_str[22:26].encode('utf-8')
|
|
#temperature = int.from_bytes(bytearray.fromhex(data_str[22:26]),byteorder='big') / 10.
|
|
global measurements
|
|
measurement = Measurement(0,0,0,0,0,0,0,0)
|
|
if args.influxdb == 1:
|
|
measurement.timestamp = int((time.time() // 10) * 10)
|
|
else:
|
|
measurement.timestamp = int(time.time())
|
|
|
|
|
|
#temperature = int(data_str[22:26],16) / 10.
|
|
temperature = int.from_bytes(bytearray.fromhex(data_str[22:26]),byteorder='big',signed=True) / 10.
|
|
print("Temperature: ", temperature)
|
|
humidity = int(data_str[26:28], 16)
|
|
print("Humidity: ", humidity)
|
|
batteryVoltage = int(data_str[30:34], 16) / 1000
|
|
print ("Battery voltage:", batteryVoltage,"V")
|
|
print ("RSSI:", rssi, "dBm")
|
|
|
|
if args.battery:
|
|
batteryPercent = int(data_str[28:30], 16)
|
|
print ("Battery:", batteryPercent,"%")
|
|
measurement.battery = batteryPercent
|
|
measurement.humidity = humidity
|
|
measurement.temperature = temperature
|
|
measurement.voltage = batteryVoltage
|
|
measurement.rssi = rssi
|
|
|
|
if mac in sensors:
|
|
try:
|
|
measurement.sensorname = sensors[mac]["sensorname"]
|
|
except:
|
|
measurement.sensorname = mac
|
|
if "offset1" in sensors[mac] and "offset2" in sensors[mac] and "calpoint1" in sensors[mac] and "calpoint2" in sensors[mac]:
|
|
measurement.humidity = calibrateHumidity2Points(humidity,int(sensors[mac]["offset1"]),int(sensors[mac]["offset2"]),int(sensors[mac]["calpoint1"]),int(sensors[mac]["calpoint2"]))
|
|
print ("Humidity calibrated (2 points calibration): ", measurement.humidity)
|
|
elif "humidityOffset" in sensors[mac]:
|
|
measurement.humidity = humidity + int(sensors[mac]["humidityOffset"])
|
|
print ("Humidity calibrated (offset calibration): ", measurement.humidity)
|
|
else:
|
|
measurement.sensorname = mac
|
|
if(args.callback):
|
|
measurements.append(measurement)
|
|
#print("Length:", len(measurements))
|
|
print("")
|
|
|
|
if args.watchdogtimer:
|
|
keepingLEScanRunningThread = threading.Thread(target=keepingLEScanRunning)
|
|
keepingLEScanRunningThread.start()
|
|
logging.debug("keepingLEScanRunningThread started")
|
|
|
|
|
|
|
|
# Blocking call (the given handler will be called each time a new LE
|
|
# advertisement packet is detected)
|
|
parse_le_advertising_events(sock,
|
|
handler=le_advertise_packet_handler,
|
|
debug=False)
|
|
except KeyboardInterrupt:
|
|
disable_le_scan(sock)
|