Releases: dcondrey/location-tracker
v2.1.3
Fixed
- Dashboard 500 error:
_analyze_movement()in dashboard.py also had the timezone-naive vs timezone-aware datetime crash. All timestamp parsing in dashboard.py now uses_safe_parse_ts(). - CUSTOM_URL includes port: Browser now opens
http://tracker.local:7070which works reliably without pf forwarding. - Bobby's data now visible: The API was returning 500 on every
/api/locationsrequest due to the timezone bug. Now returns data correctly.
v2.1.2
Fixed
- Port 80 forwarding now actually works during setup: The
pfctl -efcommand hadcapture_output=Truewhich silently swallowed the sudo password prompt, so the rules were never loaded. Removedcapture_outputso sudo can prompt the user. - Verification step: Setup now tests port 80 after starting the daemon and reports whether
http://tracker.localis working. If not, it shows the fallback URL with the port number. - Cleanup on dns-remove: Also unloads and removes the pf LaunchDaemon.
v2.1.1
Fixed
- Timezone crash: Old location data without timezone info caused
TypeError: can't subtract offset-naive and offset-aware datetimesin the intelligence engine. All timestamp parsing now normalizes to UTC. - Port 80 survives reboot: Setup now installs a LaunchDaemon (
/Library/LaunchDaemons/com.locationtracker.pf.plist) that loads pfctl rules at boot. No more manualsudo pfctlafter restart. - WSGI warning suppressed: The "do not use development server" Flask warning no longer appears in logs.
Upgrade
uv tool uninstall location-tracker && uv tool install location-tracker
location-tracker setup # re-run to install the boot LaunchDaemonv2.1.0 - Local Road Snapping + Geofencing
v2.1.0 - Local Road Snapping, Geofencing, Route Corridors
Local Map Matching (replaces OSRM)
No more third-party API dependency. Road snapping now runs locally using OpenStreetMap data via osmnx + networkx:
- Downloads the OSM road network for the tracking area on first use (~5 seconds)
- Caches the graph locally (~5MB) for instant subsequent loads
- Snaps GPS traces to actual roads using shortest-path routing between nearest nodes
- Automatically expands coverage when tracking moves to new areas
- Server-side
/api/snapendpoint; frontend calls local API
Quality improvement: Shortest-path routing between GPS nodes produces more accurate road-following than OSRM's match API, especially at intersections and complex junctions.
Geofencing
Set up virtual boundaries around locations and get notified when people enter or leave:
# Add a geofence
location-tracker geofence add "Bob" "Home" 32.745 -117.155 --radius 200
# List active geofences
location-tracker geofence list
# View events
location-tracker geofence eventsAlso available via REST API: GET/POST /api/geofences, GET /api/geofence-events
Route Corridors
The learning engine now tracks complete trips between known places:
- Records trip duration, distance, and speed profiles
- Predicts route duration from weighted historical observations
- Predicts likely destination when someone departs a known place
- Speed profiles stored per trip for future turn-zone detection
DB Migration
Automatic migration to schema v4 adds: geofences, geofence_events, route_corridors, route_observations tables.
Upgrade
uv tool uninstall location-tracker && uv tool install location-trackerv2.0.0 - Intelligent Tracking
v2.0.0 - Intelligent Learning-Based Tracking
Major release: the polling system now learns from historical behavior and adapts automatically.
Distance-Based Polling
Replaces fixed time-based tiers with distance-aware intervals. At city speeds (~30 km/h), points are captured every ~50 meters (~6 seconds), ensuring paths follow actual roads and capture every turn.
| Speed | Point Spacing | Interval |
|---|---|---|
| Walking (5 km/h) | 20m | ~14s |
| City (25 km/h) | 50m | ~7s |
| Suburban (50 km/h) | 100m | ~7s |
| Highway (100 km/h) | 150m | ~5s |
Learning Engine
New intelligence.py module that learns from tracking history:
- Known Places: Clusters repeated stops within 100m into identified locations
- Dwell Patterns: Learns how long someone typically stays at each place by day-of-week
- Departure Prediction: Computes P(departs in next 10 min) and starts aggressive polling before movement begins
- Speed Zones: Learns turn/deceleration regions and polls more aggressively when approaching them
- Statistical Weighting: All predictions require 3+ observations with exponential decay (14-day half-life)
- Cold Start: Automatically backfills from existing location history on first run
Road Snapping
Dashboard now uses OSRM map-matching API to snap GPS traces to actual roads. Raw points shown immediately at low opacity; road-snapped path replaces them async.
Battery-Aware Throttling
Reduces polling frequency when the tracked phone's battery is low (1.5x at 15-30%, 2.5x at 5-15%, 5x below 5%).
Stats
- 51 tests passing
- 3 new DB tables (known_places, dwell_observations, speed_zones)
- Automatic migration from v1.x databases
Upgrade
uv tool uninstall location-tracker && uv tool install location-tracker
location-tracker on # DB auto-migrates, backfills intelligencev1.7.0
v1.7.0 - Intelligent Adaptive Polling
New Movement Analysis Engine
Replaced the simple 4-tier speed check with multi-signal movement analysis that examines up to 10 recent points per person:
| State | Interval | How it's detected |
|---|---|---|
| Departing | 10s | Speed <10 km/h but accelerating (catches the moment someone leaves) |
| Highway | 15s | Speed >60 km/h |
| Arriving | 20s | Speed <10 km/h and decelerating (catches the moment someone arrives) |
| Driving | 30s | Speed 10-60 km/h |
| Just stopped | 30s | <2 min stationary (might resume) |
| Walking | 45s | Speed 1-10 km/h, steady pace |
| Recently stopped | 2 min | 2-10 min stationary (probably parked) |
| Stationary | 5 min | 10-30 min stationary (settled in) |
| Long stationary | 10 min | >30 min stationary (not going anywhere) |
Key improvements
- Acceleration detection: Compares first-half vs second-half speeds across recent points
- Progressive stationary backoff: 30s → 2min → 5min → 10min based on how long someone has been still
- Per-person analysis: Each tracked person is analyzed independently; the fastest-moving person determines the interval
- Departure/arrival detection: Catches transitions with aggressive polling when movement starts or stops
Upgrade
uv tool uninstall location-tracker && uv tool install location-trackerv1.6.2
Fixed
location-tracker onno longer needs sudo: All networking (DNS + pf) is configured once duringsetupwith interactive sudo. Theoncommand just starts Flask on port 7070. macOS loads pf rules from/etc/pf.confautomatically at boot viacom.apple.pfctl, so port 80 forwarding persists across reboots without re-running setup.
How it works
setup(run once): sudo writes/etc/hosts+/etc/pf.conf+ loads ruleson(run anytime): starts Flask on 7070, no sudo needed- Reboot: macOS auto-loads pf rules →
http://tracker.localworks immediately
v1.6.1
Fixed
- http://tracker.local now actually works: Root cause was stale
port: 80in saved config from v1.4.x, and pf rules not being reloaded on each start - Config migration: Old configs with
port: 80automatically corrected to7070 - Relative paths fixed:
data_fileandcookies_filein config are now resolved to absolute paths under~/.local/share/location-tracker/ - pf reload on every start: Uses
sudo -n(non-interactive) so it works silently when credentials are cached and prints a fallback command when they're not - SQLite WAL files excluded from git
How it works now
setupruns sudo for DNS + pf configuration (prompts for password once)onstarts Flask on port 7070 (no sudo needed) and silently reloads pf if sudo is cached- macOS pf transparently redirects
tracker.local:80→localhost:7070 - If pf isn't loaded (e.g. after reboot before running setup),
http://tracker.local:7070still works directly
v1.6.0
v1.6.0 - Quality Audit
Fixed
- Missing cookies guard: Provider now fails fast with a clear message if
cookies.encdoesn't exist, instead of crashing with a cryptic error - Cookie validation order: Cookies are now validated before encrypting; previously invalid cookies could be silently encrypted
- Dead code removed: Removed unused
save_history()no-op method
Docs
- Added Data Directory section documenting where all files are stored (
~/.local/share/location-tracker/) - Added Troubleshooting section for common issues (DNS, port forwarding, cookies)
- Fixed stale port references (was "port 80 via sudo", now correctly documents pfctl forwarding)
- Updated CONTRIBUTING.md import check to include all modules
Upgrade
uv tool uninstall location-tracker && uv tool install location-trackerv1.5.2
Faster startup
- Lazy imports:
pandasandfoliumare no longer imported at startup. Flask starts in ~200ms instead of ~3s. - No wait loop: Browser opens immediately after daemon launch. The browser's own DNS resolution + connection time is enough for Flask to be ready.