diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 7ab0bcf..9a4c979 --- a/README.md +++ b/README.md @@ -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). diff --git a/build-linux.sh b/build-linux.sh new file mode 100755 index 0000000..038c565 --- /dev/null +++ b/build-linux.sh @@ -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 diff --git a/build-windows.bat b/build-windows.bat new file mode 100755 index 0000000..1b9265b --- /dev/null +++ b/build-windows.bat @@ -0,0 +1,2 @@ +pyinstaller -w -F --hidden-import tornado.web main.py +move dist/main.exe dist/navpoint-windows.exe diff --git a/icon.ico b/icon.ico new file mode 100755 index 0000000..2bc854f Binary files /dev/null and b/icon.ico differ diff --git a/icon.png b/icon.png new file mode 100755 index 0000000..5f58637 Binary files /dev/null and b/icon.png differ diff --git a/icon.svg b/icon.svg new file mode 100755 index 0000000..3d9b5cb --- /dev/null +++ b/icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/main.py b/main.py new file mode 100755 index 0000000..1919d35 --- /dev/null +++ b/main.py @@ -0,0 +1,3 @@ +import navpoint.run + +navpoint.run.run() diff --git a/navpoint/__init__.py b/navpoint/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/navpoint/__main__.py b/navpoint/__main__.py new file mode 100755 index 0000000..1919d35 --- /dev/null +++ b/navpoint/__main__.py @@ -0,0 +1,3 @@ +import navpoint.run + +navpoint.run.run() diff --git a/navpoint/content.py b/navpoint/content.py new file mode 100755 index 0000000..87ed309 --- /dev/null +++ b/navpoint/content.py @@ -0,0 +1 @@ +content = """Navpoint (waiting for phone)Navpoint Desktop waiting for connection from phone1""" diff --git a/navpoint/get_ip.py b/navpoint/get_ip.py new file mode 100755 index 0000000..1c5fa64 --- /dev/null +++ b/navpoint/get_ip.py @@ -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 diff --git a/navpoint/gui.py b/navpoint/gui.py new file mode 100755 index 0000000..6c10dfd --- /dev/null +++ b/navpoint/gui.py @@ -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() diff --git a/navpoint/local_server.py b/navpoint/local_server.py new file mode 100755 index 0000000..d8eda7e --- /dev/null +++ b/navpoint/local_server.py @@ -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() diff --git a/navpoint/phone_server.py b/navpoint/phone_server.py new file mode 100755 index 0000000..2114cae --- /dev/null +++ b/navpoint/phone_server.py @@ -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( + "Navpoint

Navpoint

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.

" + ) + + 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}" diff --git a/navpoint/run.py b/navpoint/run.py new file mode 100755 index 0000000..0415aa2 --- /dev/null +++ b/navpoint/run.py @@ -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) diff --git a/navpoint_link.kml b/navpoint_link.kml new file mode 100755 index 0000000..cacbdd8 --- /dev/null +++ b/navpoint_link.kml @@ -0,0 +1,13 @@ + + + + Navpoint + 1 + Connect to Navpoint Desktop running on the local machine to receive location updates from a phone running Navpoint Mobile. + + http://localhost:8888/navpoint.kml + onInterval + 3 + + + diff --git a/requirements.txt b/requirements.txt new file mode 100755 index 0000000..adca132 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pyqrcode +tornado