Project to set up a small HTTP endpoint for receiving OwnTracks location updates from John's phone.
Resolved 2026-06-29. POST /loc is live at https://www.critchley.biz/loc with HTTP Basic Auth. Phone is sending updates and they are being stored in SQLite on gravlax.
{"_type":"location","topic":"owntracks/john/<device-id>","lat":51.522545,"lon":-2.509061,"tst":1782649022,"acc":3,"vac":3,"alt":72,"batt":85,"conn":"m","vel":26,"tid":"00"}
The phone screenshot exposed precise co-ordinates and a device/topic identifier, so avoid publishing it unredacted.
Resolved — WSGIScriptAlias /loc added to the SSL vhost. No reverse-proxy to a separate service; runs under the existing mod_wsgi/Apache process.
# With auth (expect 204) — use fake UUID in topic for test entries
curl -i -X POST 'https://www.critchley.biz/loc' \
-u 'owntracks:<password>' \
-H 'Content-Type: application/json' \
--data '{"_type":"location","lat":51.5,"lon":-2.5,"tst":1782649022,"topic":"owntracks/test/00000000-0000-0000-0000-000000000000","acc":5,"batt":80}'
# Without auth (expect 401)
curl -i -X POST 'https://www.critchley.biz/loc' \
-H 'Content-Type: application/json' \
--data '{"_type":"location","lat":51.5,"lon":-2.5,"tst":1782649022}'
With credentials returns 204 No Content. Without credentials: 401 Unauthorized. Bad JSON or missing fields: 400 Bad Request.
# OwnTracks location receiver
WSGIScriptAlias /loc /usr/local/www/wsgi-scripts/owntracks_loc.py
<Location /loc>
AuthType Basic
AuthName "OwnTracks"
AuthUserFile /etc/apache2/.htpasswd-owntracks
Require valid-user
</Location>
import sqlite3, datetime
conn = sqlite3.connect('/var/lib/owntracks/locations.db')
for r in conn.execute('SELECT received_at, topic, lat, lon, acc, batt FROM locations ORDER BY received_at DESC LIMIT 10'):
ts = datetime.datetime.utcfromtimestamp(r[0]).strftime('%Y-%m-%d %H:%M:%S UTC')
print(ts, r[1], r[2], r[3], 'acc='+str(r[4]), 'batt='+str(r[5]))
Run as: sudo python3 <script> on gravlax (DB is owned by www-data).
Read-only JSON API added 2026-06-30. Mounted at https://www.critchley.biz/location via WSGIScriptAlias /location in the SSL vhost. Source: ~/OwnTracks/owntracks_api.py on kelp, installed to /usr/local/www/wsgi-scripts/owntracks_api.py on gravlax.
# Last 100 fixes for john's phone (default UUID filter)
GET /location?who=john
# Last N fixes
GET /location?who=john&n=20
# Specific device UUID
GET /location?who=john&device=53AECFB9-BA35-44B8-BB70-9A35D2B32500
# All devices including test entries
GET /location?who=john&device=all
# Time-bounded (n limit lifted when both given)
GET /location?who=john&start=1782649000&end=1782759000
# No who / wrong who -> 403
GET /location
Tabular dump of the SQLite database. Runs on gravlax (needs sudo for DB access).
sudo python3 ~/dump_locations.py # last 20 (default)
sudo python3 ~/dump_locations.py -n 50 # last 50
sudo python3 ~/dump_locations.py --db /path/to/other.db
Fetches today's fixes from the /location API and computes speed between consecutive points using the Haversine formula. One line per fix.
python3 ~/OwnTracks/owntracks_speed.py # today, mph, text
python3 ~/OwnTracks/owntracks_speed.py --kph # km/h
python3 ~/OwnTracks/owntracks_speed.py --ms # m/s
python3 ~/OwnTracks/owntracks_speed.py --sixel # speed-over-time graph (sixel)
python3 ~/OwnTracks/owntracks_speed.py --all # all records, not just today
python3 ~/OwnTracks/owntracks_speed.py --start EPOCH --end EPOCH
/etc/owntracks/config.json on gravlax. Maps who tokens (API access tokens) to device UUIDs. Read on every request — edit and save, no restart needed.
{
"users": {
"john": "53AECFB9-BA35-44B8-BB70-9A35D2B32500"
}
}
To add a person: add their who token and UUID to the config. The receiver (owntracks_loc.py) also uses this file to resolve a topic UUID back to a name.
Exports location data as KML (Google Earth/Maps). Includes a LineString track and individual timestamped placemarks with accuracy and battery.
python3 ~/OwnTracks/owntracks_kml.py # today to stdout
python3 ~/OwnTracks/owntracks_kml.py --output ~/tmp/today.kml
python3 ~/OwnTracks/owntracks_kml.py --all --output ~/tmp/all.kml
python3 ~/OwnTracks/owntracks_kml.py --start EPOCH --end EPOCH --output ~/tmp/range.kml
See using-gps-data for cross-project conventions (fresh-fix checks, gazetteer comparisons, PostGIS query patterns) that apply here too.
Data is dual-written to PostgreSQL (PostGIS) alongside SQLite. See location-db for schema, architecture, and the pg_query MCP tool.
Home → David Lloyd ride, 2026-07-17 — bar chart of cycling speed (bar height, km/h) vs bearing (hue, constant lightness/saturation) built from a pg_query speed/bearing window function over the ride.