This commit is contained in:
Samuel Sloniker 2024-07-21 12:44:14 -07:00
parent d52ac860d7
commit 218989cf8e
17 changed files with 229 additions and 1 deletions

34
README.md Normal file → Executable file
View File

@ -1,2 +1,34 @@
# navpoint
# Navpoint Desktop
This is a simple Python program that will run two HTTP servers. One listens on
the computer's local IP address (e.g. `192.168.x.x`), port 8888, to receive
location updates from the [Navpoint
Mobile](https://gallery.appinventor.mit.edu/?galleryid=2c18ee4d-4eed-452a-9228-de8e813820d1)
Android app. The other listens on `127.0.0.1` (i.e. `localhost`), also on port
8888, for connections from a digital globe program such as [Google Earth
Pro](https://www.google.com/earth/about), allowing that program to track the
user's location. (Navpoint is not developed or endorsed by Google.)
Navpoint Desktop has a minimal GUI consisting only of a QR code. Simply scan
the code in Navpoint Mobile to establish a connection. The phone and computer
must be on the same Wi-Fi network; if a router is not available, enabling
mobile hotspot on the phone and connecting the computer to that network should
work well. An Internet connection is not needed; Navpoint works over a LAN.
Navpoint Desktop makes the geographic data available in KML format at
`http://127.0.0.1:8888/navpoint.kml`. The `navpoint_link.kml` file in this
repository contains a link to this URL configured to update every three
seconds; simply open this file in Google Earth or another compatible program to
connect to Navpoint Desktop.
## Licensing
Navpoint Desktop is released under the [GNU General Public License
v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), except the file
`navpoint/get_ip.py`, which is released under [Creative Commons
Attribution-ShareAlike 4.0
International](https://creativecommons.org/licenses/by-sa/4.0/) due to use of
[code published on Stack Overflow](https://stackoverflow.com/a/28950776).
Navpoint Mobile is released under the [GNU General Public License
v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html).

40
build-linux.sh Executable file
View File

@ -0,0 +1,40 @@
#!/bin/sh
arch=$(uname -p)
distDir=dist/navpoint-linux-$arch
rm -rf $distDir
mkdir $distDir
pyinstaller -w -F --hidden-import tornado.web main.py
mv dist/main $distDir/navpoint
cp icon.png $distDir/navpoint.png
cat > $distDir/navpoint.desktop << HERE
[Desktop Entry]
Type=Application
Terminal=false
Name=Navpoint
Exec=/bin/sh -c "$HOME/.local/bin/navpoint"
Icon=navpoint
StartupWMClass=navpoint
HERE
cat > $distDir/install.sh << HERE
#!/bin/sh
binary_target=~/.local/bin/
desktop_target=~/.local/share/applications/
icon_target=~/.local/share/icons/
cp navpoint \$binary_target/navpoint
chmod +x \$binary_target/navpoint
cp navpoint.desktop \$desktop_target/navpoint.desktop
cp navpoint.png \$icon_target/navpoint.png
HERE
chmod +x $distDir/install.sh
tar -czf $distDir.tar.gz $distDir

2
build-windows.bat Executable file
View File

@ -0,0 +1,2 @@
pyinstaller -w -F --hidden-import tornado.web main.py
move dist/main.exe dist/navpoint-windows.exe

BIN
icon.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

BIN
icon.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

6
icon.svg Executable file
View File

@ -0,0 +1,6 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" width="80" height="80" fill="none" stroke="#D00">
<circle cx="40" cy="40" stroke-width="8" r="36" stroke="#00F"/>
<circle cx="40" cy="40" stroke-width="8" r="20" stroke="#0A0"/>
<circle cx="40" cy="40" stroke-width="8" r="4" stroke="#F00"/>
</svg>

After

Width:  |  Height:  |  Size: 311 B

3
main.py Executable file
View File

@ -0,0 +1,3 @@
import navpoint.run
navpoint.run.run()

0
navpoint/__init__.py Executable file
View File

3
navpoint/__main__.py Executable file
View File

@ -0,0 +1,3 @@
import navpoint.run
navpoint.run.run()

1
navpoint/content.py Executable file
View File

@ -0,0 +1 @@
content = """<?xml version='1.0' encoding='us-ascii'?><kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2"><Document><name>Navpoint (waiting for phone)</name><description>Navpoint Desktop waiting for connection from phone</description><open>1</open></Document></kml>"""

22
navpoint/get_ip.py Executable file
View File

@ -0,0 +1,22 @@
# Copyright 2015-2021 Stack Overflow user "fatal_error"
# Copyright 2020 Stack Overflow user "user2561747"
#
# https://stackoverflow.com/a/28950776
#
# Used under CC BY-SA 4.0: https://creativecommons.org/licenses/by-sa/4.0/
import socket
def get_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.settimeout(0)
try:
# doesn't even have to be reachable
s.connect(("10.254.254.254", 1))
IP = s.getsockname()[0]
except Exception:
IP = "127.0.0.1"
finally:
s.close()
return IP

27
navpoint/gui.py Executable file
View File

@ -0,0 +1,27 @@
import tkinter as tk
import pyqrcode
def run(link):
window = tk.Tk(className="navpoint")
window.title("Navpoint")
window.resizable(width=False, height=False)
label = tk.Label(window, text="QR code here...")
label.pack()
image = tk.BitmapImage(
data=pyqrcode.create(link).xbm(scale=int(window.winfo_fpixels("2m")))
)
image.config(background="white")
image.config(foreground="black")
label.config(image=image)
try:
window.iconbitmap("icon.ico")
except tk.TclError:
pass
window.mainloop()

23
navpoint/local_server.py Executable file
View File

@ -0,0 +1,23 @@
import threading
import asyncio
import tornado
import navpoint.content
class GetHandler(tornado.web.RequestHandler):
def get(self):
self.write(navpoint.content.content)
async def main():
app = tornado.web.Application(
[
(r"/navpoint.kml", GetHandler),
]
)
app.listen(8888, address="127.0.0.1")
await asyncio.Event().wait()
def run():
threading.Thread(target=asyncio.run, args=(main(),), daemon=True).start()

46
navpoint/phone_server.py Executable file
View File

@ -0,0 +1,46 @@
import tornado
import navpoint.get_ip
import navpoint.content
import asyncio
import secrets
import threading
class PostHandler(tornado.web.RequestHandler):
def get(self):
self.write(
"<!DOCTYPE html><html><head><title>Navpoint</title></head><body><h1>Navpoint</h1><p>The Navpoint desktop app is working, but you need to scan the QR code from the Navpoint mobile app, not open it in your browser.</p></body></html>"
)
def post(self):
navpoint.content.content = self.request.body
self.write("Navpoint")
async def main_phone(ip, token):
app = tornado.web.Application(
[
(r"/" + token, PostHandler),
]
)
app.listen(8888, address=ip)
await asyncio.Event().wait()
def run_in_thread(ip, token):
asyncio.run(main_phone(ip, token))
def run():
ip = navpoint.get_ip.get_ip()
token = secrets.token_urlsafe()
threading.Thread(
target=run_in_thread,
args=(
ip,
token,
),
daemon=True,
).start()
return f"http://{ip}:8888/{token}"

8
navpoint/run.py Executable file
View File

@ -0,0 +1,8 @@
import navpoint.local_server
import navpoint.phone_server
import navpoint.gui
def run():
navpoint.local_server.run()
link = navpoint.phone_server.run()
navpoint.gui.run(link)

13
navpoint_link.kml Executable file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<NetworkLink>
<name>Navpoint</name>
<open>1</open>
<description>Connect to Navpoint Desktop running on the local machine to receive location updates from a phone running Navpoint Mobile.</description>
<Link>
<href>http://localhost:8888/navpoint.kml</href>
<refreshMode>onInterval</refreshMode>
<refreshInterval>3</refreshInterval>
</Link>
</NetworkLink>
</kml>

2
requirements.txt Executable file
View File

@ -0,0 +1,2 @@
pyqrcode
tornado