Skip to content

Commit e41ba52

Browse files
committed
breakup into parse_signal to allow offline files
correct netsh windows parsing
1 parent ec3d10a commit e41ba52

File tree

10 files changed

+84
-41
lines changed

10 files changed

+84
-41
lines changed

README.md

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Mozilla Location Services from Python
22

3-
![Actions Status](https://github.com/scivision/mozilla-location-wifi/workflows/ci/badge.svg)
4-
[![Python versions (PyPI)](https://img.shields.io/pypi/pyversions/mozloc.svg)](https://pypi.python.org/pypi/mozloc)
3+
![Actions Status](https://github.com/scivision/mozilla-location-wifi/workflows/ci/badge.svg))
54
[![PyPi Download stats](http://pepy.tech/badge/mozloc)](http://pepy.tech/project/mozloc)
65

76
Uses command line access to WiFi information in a short, simple Mozilla Location Services with Wifi from Python.
@@ -17,14 +16,14 @@ Let us know if you're interested.
1716

1817
## Install
1918

20-
2119
Get latest release
2220

2321
```sh
2422
pip install mozloc
2523
```
2624

27-
or for latest development version
25+
or for latest development version:
26+
2827
```sh
2928
git clone https://github.com/scivision/mozilla-location-wifi/
3029
pip install -e mozilla-location-wifi/
@@ -79,26 +78,25 @@ Would like to add Bluetooth beacons.
7978
* [Alternative using Skyhook and geoclue](https://github.com/scivision/python-geoclue)
8079
* [Raspberry Pi NetworkManager](https://raspberrypi.stackexchange.com/a/73816)
8180

82-
### Windows
83-
8481
To print verbose information about nearby WiFi:
8582

86-
```posh
87-
netsh wlan show networks mode=bssid
88-
```
83+
* Windows: `netsh wlan show networks mode=bssid`
84+
* MacOS: `airport -s`
85+
* Linux: `nmcli dev wifi list`
8986

9087
### Raspberry Pi 3 / 4 / Zero W
9188

9289
Debian comes without NetworkManager by default.
93-
Be careful as you lose Wifi password etc. by this procedure
90+
Thus we recommend using Ubuntu or similar on the Raspberry Pi with this program.
91+
92+
If you do use Debian with the procedure below, you lose Wifi password and stored WiFi networks.
9493

9594
1. Install network manager and remove the old
9695
```sh
9796
apt install network-manager
9897
apt purge dhcpcd5
9998
```
100-
reboot
101-
2. upon reboot, try
99+
2. Reboot and try
102100
```sh
103101
nmcli dev wifi list
104102
```

setup.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = mozloc
3-
version = 1.2.0
3+
version = 1.3.0
44
author = Michael Hirsch, Ph.D.
55
author_email = [email protected]
66
url = https://github.com/scivision/mozilla-location-wifi
@@ -42,6 +42,7 @@ tests =
4242
lint =
4343
flake8
4444
mypy
45+
types-requests
4546
io =
4647
simplekml
4748

src/mozloc/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
from .base import log_wifi_loc
2-
from .modules import get_signal, cli_config_check
2+
from .modules import get_signal, parse_signal, cli_config_check

src/mozloc/__main__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#!/usr/bin/env python
21
"""
32
https://mozilla.github.io/ichnaea/api/geolocate.html
43
https://ichnaea.readthedocs.io/en/latest/api/geolocate.html
@@ -8,7 +7,7 @@
87
Don't abuse the API or you'll get banned (excessive polling rate)
98
"""
109

11-
from .base import log_wifi_loc
10+
from .base import log_wifi_loc, process_file
1211
from .modules import get_signal
1312

1413
import pandas
@@ -37,9 +36,13 @@ def mozilla_location():
3736
help="Mozilla location services URL--don't use this default test key",
3837
default="https://location.services.mozilla.com/v1/geolocate?key=test",
3938
)
39+
p.add_argument("-i", "--infile", help="use raw text saved from command line")
4040
p = p.parse_args()
4141

42-
log_wifi_loc(p.cadence, p.url, p.logfile)
42+
if p.infile:
43+
process_file(p.infile, mozilla_url=p.url)
44+
else:
45+
log_wifi_loc(cadence_sec=p.cadence, mozilla_url=p.url, logfile=p.logfile)
4346

4447

4548
def csv2kml():

src/mozloc/airport.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from __future__ import annotations
44
import typing as T
55
import shutil
6-
import io
76
import logging
87
import subprocess
98
import re
@@ -31,17 +30,22 @@ def cli_config_check() -> bool:
3130
return False
3231

3332

34-
def get_signal() -> list[dict[str, T.Any]]:
33+
def get_signal() -> str:
3534

3635
ret = subprocess.run([EXE, "-s"], timeout=30.0, stdout=subprocess.PIPE, text=True)
3736

3837
if ret.returncode != 0:
3938
logging.error("consider slowing scan cadence.")
4039

40+
return ret.stdout
41+
42+
43+
def parse_signal(raw: str) -> list[dict[str, T.Any]]:
44+
4145
pat = re.compile(r"\s*([0-9a-zA-Z\-\.]+)\s+([0-9a-f]{2}(?::[0-9a-f]{2}){5})\s+(-\d{2,3})")
4246
dat: list[dict[str, str]] = []
4347

44-
for line in io.StringIO(ret.stdout):
48+
for line in raw.split("\n"):
4549
mat = pat.match(line)
4650
if mat:
4751
ssid = mat.group(1)

src/mozloc/base.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
11
from time import sleep
22
from pathlib import Path
33
import logging
4+
from pprint import pprint
45

5-
from .modules import get_signal, cli_config_check
6+
from .modules import get_signal, parse_signal, cli_config_check
67
from .web import get_loc_mozilla
78

89
HEADER = "time lat lon accuracy NumBSSIDs"
910

1011

12+
def process_file(file: Path, mozilla_url: str):
13+
"""
14+
process raw data captured from NetSH etc. by user previously to a file
15+
"""
16+
17+
raw = Path(file).expanduser().read_text()
18+
dat = parse_signal(raw)
19+
pprint(dat)
20+
loc = get_loc_mozilla(dat, url=mozilla_url)
21+
22+
stat = f'{loc["t"].isoformat(timespec="seconds")} {loc["lat"]} {loc["lng"]} {loc["accuracy"]:.1f} {loc["N"]:02d}'
23+
24+
print(stat)
25+
26+
1127
def log_wifi_loc(cadence_sec: float, mozilla_url: str, logfile: Path = None):
1228

1329
if logfile:
@@ -23,12 +39,13 @@ def log_wifi_loc(cadence_sec: float, mozilla_url: str, logfile: Path = None):
2339
# nmcli errored for less than about 0.2 sec.
2440
sleep(0.5)
2541
while True:
26-
dat = get_signal()
42+
raw = get_signal()
43+
dat = parse_signal(raw)
2744
if len(dat) < 2:
2845
logging.warning(f"cannot locate since at least 2 BSSIDs required\n{dat}")
2946
sleep(cadence_sec)
3047
continue
31-
print(dat)
48+
logging.debug(dat)
3249

3350
loc = get_loc_mozilla(dat, mozilla_url)
3451
if loc is None:

src/mozloc/modules.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import sys
22

33
if sys.platform == "win32":
4-
from .netsh import cli_config_check, get_signal
4+
from .netsh import cli_config_check, get_signal, parse_signal
55
elif sys.platform == "linux":
6-
from .netman import cli_config_check, get_signal
6+
from .netman import cli_config_check, get_signal, parse_signal
77
elif sys.platform == "darwin":
8-
from .airport import cli_config_check, get_signal
8+
from .airport import cli_config_check, get_signal, parse_signal
99
else:
1010
raise ImportError(f"MozLoc doesn't work with platform {sys.platform}")

src/mozloc/netman.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def cli_config_check() -> bool:
4040
return False
4141

4242

43-
def get_signal() -> list[dict[str, T.Any]]:
43+
def get_signal() -> str:
4444

4545
ret = subprocess.run(NMCMD, timeout=1.0)
4646
if ret.returncode != 0:
@@ -51,8 +51,13 @@ def get_signal() -> list[dict[str, T.Any]]:
5151
if ret.returncode != 0:
5252
logging.error("consider slowing scan cadence.")
5353

54+
return ret.stdout
55+
56+
57+
def parse_signal(raw: str) -> list[dict[str, T.Any]]:
58+
5459
dat = pandas.read_csv(
55-
io.StringIO(ret.stdout),
60+
io.StringIO(raw),
5661
sep=r"(?<!\\):",
5762
index_col=False,
5863
header=0,

src/mozloc/netsh.py

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
import typing as T
55
import subprocess
66
import logging
7-
import shutil
87
import io
9-
from math import log10
8+
import shutil
109

1110
CLI = shutil.which("netsh")
1211
if not CLI:
@@ -29,14 +28,24 @@ def cli_config_check() -> bool:
2928
return False
3029

3130

32-
def get_signal() -> list[dict[str, T.Any]]:
33-
""" get signal strength using CLI """
31+
def get_signal() -> str:
32+
"""
33+
get signal strength using CLI
34+
35+
returns dict of data parsed from CLI
36+
"""
3437
ret = subprocess.run(CMD, timeout=1.0, stdout=subprocess.PIPE, text=True)
3538
if ret.returncode != 0:
3639
logging.error("consider slowing scan cadence.")
3740

41+
return ret.stdout
42+
43+
44+
def parse_signal(raw: str) -> list[dict[str, T.Any]]:
45+
3846
dat: list[dict[str, str]] = []
39-
out = io.StringIO(ret.stdout)
47+
out = io.StringIO(raw)
48+
4049
for line in out:
4150
d: dict[str, str] = {}
4251
if not line.startswith("SSID"):
@@ -45,6 +54,7 @@ def get_signal() -> list[dict[str, T.Any]]:
4554
# optout
4655
if ssid.endswith("_nomap"):
4756
continue
57+
4858
# find BSSID MAC address
4959
for line in out:
5060
if not line[4:9] == "BSSID":
@@ -62,14 +72,20 @@ def get_signal() -> list[dict[str, T.Any]]:
6272
d["ssid"] = ssid
6373
dat.append(d)
6474
d = {}
75+
# need break at each for level
6576
break
77+
break
6678

6779
return dat
6880

6981

7082
def signal_percent_to_dbm(percent: int) -> int:
71-
"""arbitrary conversion factor from Windows WiFi signal % to dBm
72-
assumes 100% is -30 dBm
83+
"""
84+
arbitrary conversion factor from Windows WiFi signal % to dBm
85+
assumes signal percents map to dBm like:
86+
87+
* 100% is -30 dBm
88+
* 0% is -100 dBm
7389
7490
Parameters
7591
----------
@@ -81,9 +97,8 @@ def signal_percent_to_dbm(percent: int) -> int:
8197
meas_dBm: int
8298
truncate to nearest integer because of uncertainties
8399
"""
84-
REF = -30 # dBm
85-
ref_mW = 10 ** (REF / 10) / 1000
86-
meas_mW = max(ref_mW * percent / 100, 1e-7)
87-
meas_dBm = 10 * log10(meas_mW) + 30
88100

89-
return int(meas_dBm)
101+
REF = -100 # dBm
102+
assert 0 <= percent <= 100, "percent must be 0...100"
103+
104+
return int(REF + percent * 7 / 10)

src/mozloc/tests/test_netman.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
def test_nm_loc():
99

1010
mozloc = pytest.importorskip("mozloc")
11-
loc = mozloc.get_signal()
11+
loc = mozloc.parse_signal(mozloc.get_signal())
1212

1313
assert isinstance(loc, list)
1414
assert isinstance(loc[0], dict)

0 commit comments

Comments
 (0)