Merge pull request #209 from jennypaxian/main
Initial support for iBeacon BLE scanning for FakeMobileClient
This commit is contained in:
commit
bbb0e9279b
2 changed files with 71 additions and 1 deletions
4
tools/FAKEMOBILECLIENT.TXT
Normal file
4
tools/FAKEMOBILECLIENT.TXT
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
For Wifi Scanning you need wireless_tools
|
||||||
|
|
||||||
|
For Bluetooth Scanning you need BlueZ bluetooth + Dev Lib libbluetooth and pip install bleak
|
||||||
|
Edit /lib/systemd/system/bluetooth.service and add --experimental to ExecStart Line for bluetoothd
|
|
@ -4,9 +4,31 @@ import socketserver
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import struct
|
||||||
|
import math
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
|
from construct import Array, Byte, Const, Int8sl, Int16ub, Struct
|
||||||
|
from construct.core import ConstError
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from bleak import BleakScanner
|
||||||
|
from bleak.backends.device import BLEDevice
|
||||||
|
from bleak.backends.scanner import AdvertisementData
|
||||||
|
|
||||||
|
|
||||||
PORT = int(sys.argv[1]) if sys.argv[1:] else 8042
|
PORT = int(sys.argv[1]) if sys.argv[1:] else 8042
|
||||||
|
|
||||||
|
ibeacon_format = Struct(
|
||||||
|
"type_length" / Const(b"\x02\x15"),
|
||||||
|
"uuid" / Array(16, Byte),
|
||||||
|
"major" / Int16ub,
|
||||||
|
"minor" / Int16ub,
|
||||||
|
"power" / Int8sl,
|
||||||
|
)
|
||||||
|
|
||||||
def get_from_lines(lines, keyword):
|
def get_from_lines(lines, keyword):
|
||||||
try:
|
try:
|
||||||
|
@ -14,6 +36,48 @@ def get_from_lines(lines, keyword):
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def calc_distance(txPower, rssi):
|
||||||
|
if (rssi == 0):
|
||||||
|
return -1.0
|
||||||
|
|
||||||
|
ratio = rssi*1.0/txPower;
|
||||||
|
if (ratio < 1.0):
|
||||||
|
distance = math.pow(ratio,10);
|
||||||
|
else:
|
||||||
|
distance = (0.89976) * math.pow(ratio,7.7095) + 0.111;
|
||||||
|
|
||||||
|
return distance;
|
||||||
|
|
||||||
|
|
||||||
|
async def ble_scan():
|
||||||
|
beacons = []
|
||||||
|
|
||||||
|
devices = await BleakScanner.discover(return_adv=True)
|
||||||
|
|
||||||
|
devices = OrderedDict(
|
||||||
|
sorted(devices.items(), key=lambda x: x[1][1].rssi, reverse=True)
|
||||||
|
)
|
||||||
|
|
||||||
|
for i, (addr, (device, advertisement_data)) in enumerate(devices.items()):
|
||||||
|
try:
|
||||||
|
apple_data = advertisement_data.manufacturer_data[0x004C]
|
||||||
|
ibeacon = ibeacon_format.parse(apple_data)
|
||||||
|
uuid = UUID(bytes=bytes(ibeacon.uuid))
|
||||||
|
beacons.append({'uuid': str(uuid), 'major': ibeacon.major, 'minor': ibeacon.minor, 'distance': calc_distance(ibeacon.power, advertisement_data.rssi), 'last_seen_ago': 0 })
|
||||||
|
except KeyError:
|
||||||
|
# Apple company ID (0x004c) not found
|
||||||
|
pass
|
||||||
|
except ConstError:
|
||||||
|
# No iBeacon (type 0x02 and length 0x15)
|
||||||
|
pass
|
||||||
|
|
||||||
|
return beacons
|
||||||
|
|
||||||
|
def async_to_sync(awaitable):
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
return loop.run_until_complete(awaitable)
|
||||||
|
|
||||||
class FakeMobileClientHandler(http.server.BaseHTTPRequestHandler):
|
class FakeMobileClientHandler(http.server.BaseHTTPRequestHandler):
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
"""Serve a GET request."""
|
"""Serve a GET request."""
|
||||||
|
@ -51,11 +115,13 @@ class FakeMobileClientHandler(http.server.BaseHTTPRequestHandler):
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
beacons = async_to_sync(ble_scan())
|
||||||
|
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header('Content-Type', 'application/json')
|
self.send_header('Content-Type', 'application/json')
|
||||||
self.send_header('Access-Control-Allow-Origin', '*')
|
self.send_header('Access-Control-Allow-Origin', '*')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(json.dumps({'wifi':stations}).encode())
|
self.wfile.write(json.dumps({'wifi':stations, 'ibeacon':beacons}).encode())
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue