added python script to locate linux devices with the wifi location api
optionally updates the coordinates of a custom position
This commit is contained in:
parent
3988fff5e6
commit
c242799566
1 changed files with 123 additions and 0 deletions
123
tools/locate.py
Normal file
123
tools/locate.py
Normal file
|
@ -0,0 +1,123 @@
|
|||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
def get_from_lines(lines, keyword):
|
||||
return next(iter(l for l in lines if l.startswith(keyword))).split(keyword)[1].strip()
|
||||
|
||||
|
||||
def scan(interface=None, sudo=False):
|
||||
command = []
|
||||
if sudo:
|
||||
command.append('sudo')
|
||||
command.append('iwlist')
|
||||
if interface:
|
||||
command.append(interface)
|
||||
command.append('scan')
|
||||
|
||||
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
output = p.communicate()[0].decode().split('Cell')[1:]
|
||||
if not output:
|
||||
return None
|
||||
|
||||
stations = []
|
||||
for data in output:
|
||||
lines = [l.strip() for l in data[5:].split('\n')]
|
||||
try:
|
||||
stations.append({
|
||||
'bssid': get_from_lines(lines, 'Address:'),
|
||||
'ssid': get_from_lines(lines, 'ESSID:')[1:-1],
|
||||
'level': int(get_from_lines(lines, 'Quality=').split('=')[-1][:-4]),
|
||||
'frequency': int(float(get_from_lines(lines, 'Frequency:').split(' ')[0]) * 1000)
|
||||
})
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
if not stations:
|
||||
return []
|
||||
return stations
|
||||
|
||||
|
||||
def locate(instance, interface=None, sudo=False, secret=None, api_secret=None, location_timeout=None):
|
||||
stations = scan(interface=interface, sudo=sudo)
|
||||
if not stations:
|
||||
return None
|
||||
|
||||
url = instance
|
||||
if url.endswith('/'):
|
||||
url = url[0:-1]
|
||||
url += '/api/routing/locate/'
|
||||
|
||||
if bool(secret) != bool(api_secret):
|
||||
raise ValueError('secret specified but not api_secret, or vice versa')
|
||||
|
||||
json = {
|
||||
'stations': stations,
|
||||
}
|
||||
|
||||
if secret and api_secret:
|
||||
if not secret.startswith('p:'):
|
||||
secret = 'p:' + secret
|
||||
|
||||
json['set_position'] = secret
|
||||
json['secret'] = api_secret
|
||||
|
||||
if location_timeout:
|
||||
json['timeout'] = int(location_timeout)
|
||||
|
||||
r = requests.post(url, json=json)
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='locate yourself via the c3nav api and optionally update the coordinates of a custom position '
|
||||
'in c3nav')
|
||||
parser.add_argument('--instance', '-i', metavar='https://event.c3nave.de',
|
||||
required=not bool(os.environ.get('C3NAV_INSTANCE', False)),
|
||||
default=os.environ.get('C3NAV_INSTANCE'),
|
||||
help='The base url of the c3nav instance to use, for example https://36c3.c3nav.de.')
|
||||
parser.add_argument('--secret', '-s', default=os.environ.get('C3NAV_SECRET'),
|
||||
help='The secret of a custom position. If specified the custom position belonging to the the '
|
||||
'secret will be updated')
|
||||
parser.add_argument('--apisecret', '-a', default=os.environ.get('C3NAV_APISECRET'),
|
||||
help='The api secret of a custom position, needed if you want to update a custom position')
|
||||
parser.add_argument('--timeout', '-t', metavar='600', default=os.environ.get('C3NAV_LOCATION_TIMEOUT'), type=int,
|
||||
help='Sets this timeout in seconds on the update to the coordinates of a custom position.')
|
||||
parser.add_argument('--sudo', action='store_true', default=bool(os.environ.get('C3NAV_USE_SUDO', False)),
|
||||
help='use sudo, i.e. "sudo iwlist scan"')
|
||||
parser.add_argument('--interface', metavar='wlp1s0', default=os.environ.get('C3NAV_SCAN_INTERFACE'),
|
||||
help='Scan use this interface. By default all interfaces are used')
|
||||
parser.add_argument('--quiet', '-q', action='store_true', help='don\'t output server response')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.interface and not re.fullmatch(r'[a-zA-Z0-9_-]+', args.interface):
|
||||
raise argparse.ArgumentError('invalid character in interface name')
|
||||
|
||||
if args.secret and ' ' in args.secret:
|
||||
raise argparse.ArgumentError('invalid character in secret')
|
||||
|
||||
if args.apisecret and ' ' in args.apisecret:
|
||||
raise argparse.ArgumentError('invalid character in apisecret')
|
||||
|
||||
if bool(args.secret) != bool(args.apisecret):
|
||||
raise argparse.ArgumentError('secret specified but not apisecret, or vice versa')
|
||||
|
||||
location = locate(args.instance, interface=args.interface, sudo=args.sudo, secret=args.secret,
|
||||
api_secret=args.apisecret, location_timeout=args.timeout)
|
||||
|
||||
if location['location'] is None:
|
||||
# no location found
|
||||
if not args.quiet:
|
||||
print('no location found')
|
||||
exit(1)
|
||||
elif not args.quiet:
|
||||
print(json.dumps(location, sort_keys=True, indent=4))
|
Loading…
Add table
Add a link
Reference in a new issue