Table of Contents
- Key Highlights
- Introduction
- What the app does and how a typical session unfolds
- Architecture and the single most important design decision
- How weather is fetched, interpreted and turned into scores
- Routing: wind-aware loops from your door
- The role of the model: Gemma 4 E2B as coach and conversational planner
- Open innovation and self-hosting: why it matters practically
- Practical deployment and the developer experience
- Offline, caching and watch usage
- Real-world scenarios: how the app changes decisions and outcomes
- Trade-offs, limitations and where human judgment still matters
- How to extend the project: integrations and future directions
- A technical deep dive: the key components explained
- Design lessons and takeaways
- How to try it and what you need
- Where the project sits in the broader landscape
- FAQ
Key Highlights
- A self-hosted app that checks open weather data, scores run/ride/walk options, generates a wind-aware route and produces a tailored workout plan — all while keeping routing, weather and the model under your control.
- Design splits responsibilities: deterministic Go rules handle numerical scoring and safety decisions; a small local LLM (Gemma 4 E2B in the prototype) turns structured facts into a human-friendly coaching plan and handles follow-ups.
- Uses open-source building blocks — Open-Meteo, Overpass, BRouter, OpenStreetMap and any OpenAI-compatible model server — so it runs without per-request bills, adapts when services fail, and delivers GPX routes for offline use.
Introduction
People who exercise outdoors lose more than motivation when the weather is uncertain: they lose minutes, attention and often the entire session while checking forecasts, wind, trail conditions and local routes. A forecast app lists rain and wind but rarely answers the single practical question that matters: should I go out now, and if so, what should I do and where?
workout-suggester was built to collapse that decision into one quick interaction. Tap “Here”, pick activity, intensity and available time, and the app returns immediate, actionable guidance: a numerical score for run/ride/walk in the coming hours, transparent reasons for each score, a recommended session (warm‑up, main set, cool‑down), a looped route that starts and ends at your door and deliberately sends you into the wind so your return benefits from a tailwind, plus a GPX you can load on your watch. If conditions are genuinely unsuitable, it hands you an indoor HIIT plan instead.
This article examines the principles that shaped the project, the architecture and trade-offs behind critical decisions, how the app uses open data and a small local model to produce human-grade guidance, practical scenarios showing how people will use it, and how to run and extend the system yourself.
What the app does and how a typical session unfolds
The user interaction focuses on speed and minimal friction. On a phone you open the app, tap “📍 Here”, select run/ride/walk or “surprise me”, choose intensity and time budget, then let it do the work. A few seconds later you get:
- A short forecast window (next hour or two) with a 0–100 score for run, ride and walk. Each score includes plain-language reasons: “strong gusts up to 50 km/h,” “dark: bring lights,” “heavy showers,” or “clear afternoon at 17:00.”
- A clear recommendation: what activity to do, the best start time if waiting yields a better daylight/wind window, a structured session that fits your chosen intensity and time (warm-up → main set → cool-down), clothing suggestions and a plan B.
- A route: a loop of the right length that begins and ends at your door, with three alternative directions and a route that intentionally starts into the wind. Routes list climb, paved percentage and wind direction; signposted regional routes and OSM knooppunt junctions nearby are included. Each route has a GPX download for watches or bike computers.
- Follow-up actions: “make it harder,” “only 30 minutes,” or “prefer paved” adjust the plan and re-generate a fresh GPX quickly.
The app aims for the moment between deciding to exercise and stepping outside. It reduces that friction to about a minute: pick, tap, strap on your watch and go.
Architecture and the single most important design decision
Two principles determined the architecture: keep the stack small and local-first, and never let the language model make deterministic numeric or safety decisions.
A concise summary of the stack:
- Front end: small server-rendered templates (Templ) with HTMX for progressive enhancement and server-sent events for streamed model output.
- Map and routing UI: Leaflet for interactive maps, BRouter to create round trips, Overpass to fetch signposted OSM routes.
- Weather and forecasts: Open-Meteo as first choice with MET Norway as fallback (and local sunset/sunrise calculations to avoid model hallucinations).
- Model: Any OpenAI-compatible server. The prototype uses Gemma 4 E2B running locally via oMLX, with the app streaming the model’s reply.
- Core logic: Go binary (~9 MB), responsible for fetching, scoring and routing decisions.
The single most important decision was explicit: do not rely on the LLM to perform numeric scoring or safety checks. Small local models hallucinate numbers and invent facts—wrong gust speeds, incorrect sunset times, and sometimes unsafe suggestions. To avoid that, workout-suggester partitions responsibilities:
-
Deterministic data and logic live outside the model.
- Weather data comes from Open-Meteo (fallback MET Norway), raw numerical fields are used directly.
- Scoring rules are plain Go code: each activity starts at 100 and loses points according to predefined weights per condition.
- Route generation uses BRouter and Overpass queries; metrics like climb, paved percentage and wind direction are computed explicitly.
-
The LLM receives a short, structured system prompt containing the facts computed by Go: the weather window, the scores and reasons, the selected route characteristics, and the chosen time/distance/pacing. It generates human-friendly explanations, assembles the workout session, and handles conversational follow-ups. The model never decides whether something is safe; it explains the choice that the rules produced.
This split yields two benefits. First, safety-critical and numerically precise logic is fully deterministic and auditable. Second, the model is used for the tasks it handles well — crafting readable coaching plans and responding to follow-ups — while avoiding the risks of model hallucination.
How weather is fetched, interpreted and turned into scores
Accurate weather inputs matter more than a clever language model when the decision is “go outside or not.” Open, auditable weather data allows the app to apply predictable rules and to failover gracefully.
Source and fallbacks
- Primary: Open-Meteo. No API key required, open source and contains hourly fields for wind speed, gusts, precipitation, cloud cover and more.
- Secondary: MET Norway (api.met.no) as a fallback for when Open-Meteo is unreachable.
- Local calculation: sunrise and sunset times are computed locally to avoid model hallucinations about daylight.
Gust handling and estimation MET Norway does not provide gust measurements for Belgium in the same way Open-Meteo does. When gust fields are absent, the app estimates gusts from sustained wind speed using a conservative formula and marks these estimates with an approximation symbol (≈) so the user sees there is derived data.
A simple ruleset and transparent weights Every activity (run, ride, walk) starts at 100 points. Go rules subtract points based on adverse conditions; different activities receive different penalties for the same condition. Examples of scoring decisions used in the prototype:
- Gusts over 45 km/h: ride −40, run −15, walk −10.
- Heavy rain: large negative penalty to all activities (the exact value depends on precipitation intensity).
- Thunder: flat zero for all activities (safety override).
- Darkness: penalty that depends on whether the route has lighting and the user’s activity (riders need brighter lights; runners may prefer reflective clothing).
If all activities score below 40, the suggestion is to move inside with a concrete HIIT alternative. The app also looks ahead up to 12 hours: if there’s a clearly better daylight or wind window later, it will recommend waiting and propose a specific start time instead of a blanket “don’t go.”
Why rules beat the model for scoring LLMs frequently invent numerical facts or misinterpret units. A model might call a 50 km/h gust a “light breeze” or misstate sunset time. The Go rules are simple to review and tweak, and they ensure consistent, safety-conscious recommendations.
Routing: wind-aware loops from your door
Finding a useful loop that matches desired distance, handles wind intentionally, respects surface preferences and prefers nearby signposted trails requires more than a blind “generate round trip” call.
BRouter and round-trip quirks BRouter generates round trips from a start point and a distance parameter. In practice, BRouter’s round-trip parameter behaves more like a radius than a strict distance: asking for a 10 km round-trip could return an unexpectedly long route. workout-suggester detects this and scales the generated route to the target distance, discarding implausible long-distance trails like 130 km Streek-GRs.
Start into the wind A small design detail has outsized practical impact: the app intentionally starts the loop into the wind so the return leg tends to have a tailwind. For cyclists this turns a demotivating headwind finish into a comfortable ride home. For runners it reduces the fatigue-adjusting return leg. The app computes wind direction for the user’s location and orients the round-trip generation accordingly.
Surface, climb and route metadata Each candidate route is annotated with:
- Total ascent/descent.
- Percentage paved vs unpaved.
- Elevation profile to inform pacing recommendations.
- Proximity to signposted OpenStreetMap routes or knooppunt junctions.
Overpass queries detect nearby signposted cycling and walking networks. The app caches these results locally for a week so users can complete a planned route if they lose phone signal during the session. GPX export is available for every route, letting watches and bike computers navigate the path without a live connection.
Real-world routing examples
- Commuter cyclist in Flanders: 20 km time budget, gusty NW winds. The app generates three loops: east, south, west. It prioritizes a westward route with a headwind leaving town and a tailwind home, routes through signposted fietsroutes and avoids unpaved farm tracks. The score penalizes gusts but still recommends going because headwind is manageable out and tailwind return boosts overall comfort.
- Weekend trail runner: 90 minutes available, forecast shows afternoon thunderstorms. The app finds a nearby forest loop, penalizes lightning heavily and recommends moving the session earlier to a clear 2-hour window at 10:00 or else offers an indoor threshold run if the timing can’t change.
The role of the model: Gemma 4 E2B as coach and conversational planner
Gemma 4 E2B (gemma-4-E2B-it-qat-4bit in the prototype) runs locally in the demo. The model’s job is narrowly defined: turn structured facts into coach-speak, assemble a session, explain the reasons behind the pick and react to follow-ups like “make it harder” or “I only have 30 minutes.”
Why a local small model?
- Latency: local models allow the app to stream responses in about 10 seconds on a typical laptop.
- Privacy and control: local inference avoids sending personal location and activity preferences to cloud services.
- Cost: no per-token bill for queries you run every morning.
The model receives a compact system prompt containing:
- The weather window and the specific conditions (wind speeds, gusts, precipitation, darkness).
- The three activity scores and the human-readable reasons for each.
- Details of the chosen route(s): distance, climb, paved %, wind direction and estimated pace for the target intensity.
- The user’s selected time budget and effort.
The LLM uses that sheet of facts to:
- Produce a short explanation of the pick that a human coach would give.
- Build a workout plan (warm-up, main set, cool-down) appropriate to the selected intensity and duration, including pacing or cadence cues and suggested progressions.
- Suggest clothing and gear (e.g., “bring lightweight windproof, lights if starting before dusk”).
- Generate plan B options (short HIIT inside, alternative route, delay suggestion).
Crucially, the model does not compute numbers used in safety decisions. It explains the decision already determined by the rules. That keeps hallucinations out of safety-critical paths.
Streaming and follow-ups The app streams the model’s generated plan via server-sent events so the user sees the plan appear progressively. Follow-up edits carry over the factual sheet with updated constraints (new time, different intensity or surface preference), and the model produces a revised plan quickly.
Open innovation and self-hosting: why it matters practically
workout-suggester is built almost entirely from components that can be self-hosted. That is a calculated product decision with practical consequences.
Swapability reduces vendor risk Each external dependency is referenced by an environment variable: LLM_BASE_URL, LLM_MODEL and endpoints for Open-Meteo/Overpass/BRouter. If a service fails or is rate-limited, you point the app at another compatible instance or self-host one. This is exactly what happened during the build: Open-Meteo experienced trouble and a fallback to MET Norway was added as a quick, isolated change rather than a contractual or billing negotiation.
No per-query bill for daily queries A cloud LLM that charges per token quickly becomes expensive for an app you query multiple times every day. A local or self-hosted LLM eliminates per-query cost and gives the developer control over model upgrades. Swap E2B for E4B by changing an environment variable; no API keys or pricing pages required.
Local copies of route and map metadata OpenStreetMap’s volunteer-created signposted routes are a major advantage here. Flanders and similar regions have detailed mapped fietsroutes, wandelroutes and junction networks; these appear in Overpass queries and make local routing much better than closed, proprietary fitness APIs. Caching signposted routes on disk for a week allows the app to produce GPX files and complete planned sessions without an active internet connection.
Privacy and data minimization Because the system can run locally, personal location and route preferences don’t need to leave the user’s machine. That aligns with privacy-conscious designs and avoids storing personal workout histories in cloud vendor platforms.
Cost of ownership The app runs as a single ~9 MB Go binary and uses tools whose operational cost is controllable by the user. That reduces the barrier to entry for hobbyist deployers and organizations that prefer self-hosting.
Practical deployment and the developer experience
Getting the prototype running is deliberately simple for anyone comfortable with a terminal. The code repository is available at git.b0b.be/bdeb/workout-suggester and the compiled binary is compact. The minimal steps used in the demo:
- Copy the example environment file and set your model server:
cp .env.example .env
LLM_BASE_URL=http://127.0.0.1:8000/v1 (oMLX), or llama.cpp, Ollama, LM Studio...
- Run:
make run
→ http://127.0.0.1:3000
Running the app requires an OpenAI-compatible model server endpoint. The project demonstrated Gemma 4 E2B through oMLX on a MacBook, but any server that supports the OpenAI-compatible API will work. The stack uses HTTP for model calls and server-sent events for streaming.
Why Go? Go produces single-binary deployments, minimal runtime dependencies and predictable concurrency for I/O-heavy flows (fetching weather, Overpass, BRouter and streaming model replies). The UI uses Templ templates and HTMX to keep client-side complexity low while maintaining responsiveness.
Streaming the plan Model replies stream over server-sent events to the front end. That lets the plan appear progressively rather than waiting for a long-blocking response. On mobile this provides fast perceived speed even if the full plan takes a few seconds to assemble.
Offline, caching and watch usage
A key practical constraint for outdoor workouts is connectivity. A GPX route you can put on your watch before leaving removes the need for phone signal.
Caching behavior
- Signposted OpenStreetMap routes and Overpass results are cached on disk for one week.
- Once a route is generated and its GPX saved, you can load it onto a watch or bike computer and navigate without a data connection.
- Sunrise/sunset and the last forecast snapshot are kept so the app can still suggest something if the weather service is offline; the UI indicates the age of the forecast.
GPX workflows
- Routes generated by BRouter are exported as GPX and are compatible with most watches and bike computers.
- The app streamlines producing a GPX: pick the recommended route, click “Download GPX”, upload to your device and go.
- For frequent users, generating the GPX takes seconds after a short initial decision flow.
This combination of caching and GPX ensures the app remains useful even when outside cell coverage.
Real-world scenarios: how the app changes decisions and outcomes
Below are four realistic examples showing how workout-suggester alters behavior compared with conventional forecast-checking.
-
Commuter on a gusty morning A commuter cyclist checks the app at 07:00. Open-Meteo reports gusts up to 50 km/h from the NW for the first two hours, then calmer winds after 09:00. The app scores ride = 30, run = 55, walk = 70. It recommends walking today or delaying the ride until calmer winds, but offers a 10 km looping ride that leaves into the wind and returns with a tailwind if the rider insists on cycling. The rider decides to delay by 30 minutes and sets the suggested start at 08:10, which the app shows will have gusts under 30 km/h.
-
Weekend long ride with crossing winds A group cyclist wants a 4‑hour outing. Forecast shows variable crosswinds and rain later in the day. The app produces three route options with different exposure: a river-side route with sheltering trees but some unpaved sections, a flat open route ideal for fast speeds but exposed to crosswind, and a mixed forest loop that’s slightly longer but safer if rain starts. Because the rules penalize gusts strongly for riders, the app suggests the more sheltered mixed forest option and recommends bringing a windproof and lights because darkness may arrive on the return.
-
Quick lunchtime run in urban area A runner has 30 minutes and checks the app. It shows run score = 85 with a note: “low wind, light drizzle that will end in 20 minutes.” The app generates a 5 km loop that avoids muddy parks and includes a short main set: 3×2-minute tempo intervals inside the available time. The runner downloads GPX and squeezes the session into the lunch break.
-
Avoiding unsafe conditions An afternoon forecast includes thunder and lightning. The app applies a flat-zero policy: any thunder in the forecast window sets an immediate indoor recommendation. It provides a structured 20-minute HIIT session tailored to the user’s time budget and explains that lightning risk outweighs any outdoor benefit.
Each scenario demonstrates how transparent rules combined with human-style coaching make the decision both safer and faster.
Trade-offs, limitations and where human judgment still matters
The app reduces decision friction but does not eliminate the need for human judgment.
Model limitations
- A small local model like Gemma 4 E2B provides quick, private, inexpensive responses but lacks the accuracy of larger cloud models on some tasks. That is precisely why deterministic rules handle numeric and safety-critical steps.
- The model still might produce stylistic or tactical recommendations that require user interpretation (e.g., pacing suggestions that assume a base fitness level). The app’s approach is to present these as coach-style suggestions rather than absolute prescriptions.
Data quality
- Open weather data can lack localized microclimate detail, particularly in urban canyons or mountain valleys where small-scale conditions differ from regional models.
- OSM data quality varies by region. In areas with sparse volunteer mapping, signposted routes and knooppunt networks may be incomplete or missing.
Routing quirks
- BRouter’s round-trip behavior sometimes returns unexpectedly long paths. The app scales and filters these, but edge cases exist, particularly for very short or very long requested distances.
- Long-distance or multi-day trails may appear in results; the app filters out obvious mismatch routes but sophisticated user preferences (multi-surface constraints, seasonal closures) require further integration.
Safety and legalities
- The app is not a substitute for official closure notices or authoritative safety advisories. It does not make legal safety determinations; it makes recommendations based on public data and deterministic rules.
- Users should not rely on the app for emergency decisions. If local authorities issue a severe weather warning, obey official instructions.
Resource requirements
- Running a local LLM requires CPU/GPU resources. On a MacBook with oMLX the prototype performs well, but users with very low-powered machines will need a remote or cloud model server.
These limitations are recognized and addressed where possible via transparency: the UI shows why a score was assigned, marks estimates (e.g., gust approximations) clearly, and indicates the age of cached data.
How to extend the project: integrations and future directions
The architecture is deliberately modular, making several extensions straightforward.
Heart-rate and performance integration
- Sync past workouts and heart-rate zones (e.g., from Polar, Garmin, or FitBit) to generate more personalized session prescriptions such as tempo run targets based on threshold heart rate.
- Use power data for cyclists to produce FTP-based training sessions and more precise interval prescriptions.
Third-party service sync
- Optional Strava export or import could let users upload completed workouts for tracking.
- Integrate with navigation apps that accept GPX or follow breadcrumb tracks in real time.
Enhanced routing features
- Add surface-type preferences (avoid gravel, prefer trails) and seasonal constraints (avoid snowed-in trails).
- Route risk scoring: annotate routes by exposure to traffic, lighting, or trail width.
- Support multi-point routes that match on-the-way pickups (coffee shops, child-care drop-offs).
Better weather microdata
- Integrate hyperlocal weather sensors (personal weather stations) or microclimate sensors to refine local forecasts.
- Include road-surface wetness or ice warnings using additional data sources.
Improved model prompts and personalization
- Store a short user profile (preferred pace ranges, cadence, known health limits) locally and include that sheet of facts in the model prompt for more personalized coaching.
- Support model variants for different coaching voices: firm, encouraging, technical.
Team or club features
- Group ride planning where the score accounts for average group pace and wind exposure across multiple start points.
- Shared GPX and plan edits for club leaders.
Security and reliability
- Harden the caching and fallback logic to gracefully handle long-term outages.
- Add rate-limiting and monitoring for self-hosted model servers.
Each extension should keep the central principle: deterministic, auditable rules for safety and numeric logic; the model focused on human-facing language and follow-ups.
A technical deep dive: the key components explained
Below are more details for developers who want to understand the plumbing or reproduce the prototype.
Core binary and runtime
- Language: Go. Produces a single binary (~9 MB). Simplicity of deployment is a core goal.
- Templates and UI: Templ for server-side rendering and HTMX for client interactions. HTMX reduces client-side JavaScript and lets the server drive state transitions easily.
- Streaming: server-sent events deliver the LLM stream to the browser progressively.
Model integrations
- The code expects an OpenAI-compatible endpoint; the environment variable LLM_BASE_URL points to the server.
- The prototype used oMLX as a local server for Gemma 4 E2B. You can alternatively point to llama.cpp, Ollama or LM Studio as long as the server implements the compatible API.
- Swap models by setting the LLM_MODEL environment variable.
Weather and forecast
- Open-Meteo API provides hourly forecasts including wind_speed_10m, wind_gusts_10m, precipitation and cloud cover.
- The app computes sunrise and sunset locally from coordinates to avoid relying on model text outputs for daylight.
- If Open-Meteo is unreachable, a simple configuration switch lets the app query MET Norway.
Routing and OSM
- BRouter generates round trips and routing geometry. The app computes distance, elevation gain and paved percentages from the route geometry.
- Overpass queries extract nearby signposted OSM routes (fietsroutes/wandelroutes) and knooppunt junctions for Flanders-style networks.
- BRouter’s round-trip distance scaling is implemented to correct the typical radius-like behavior.
Scoring engine
- Implemented as pure Go rules. Each activity has a set of weighted penalties (gusts, sustained wind, precipitation intensity, darkness). The final score is max(0, 100 - total_deductions).
- Special conditions override scores: thunder = 0, severe storms force indoor suggestion.
- A simple “look-ahead” scanning routine checks up to 12 hours forward for a markedly better window and will propose delaying the session.
GPX and caching
- Generated routes are exported as GPX for watch/bike computer consumption.
- Overpass and route metadata cached for one week; the app includes cache invalidation and indicates the cache age in the UI.
Developer workflow
- Repository: git.b0b.be/bdeb/workout-suggester
- Quick start:
- cp .env.example .env
- Edit .env to point to your model server and other endpoints.
- make run
- Open http://127.0.0.1:3000
Design lessons and takeaways
A handful of lessons emerged while building a small but practical tool that people will use on the move.
-
Use the right tool for the right task
- LLMs are excellent at producing natural language and adapting a plan to conversational edits. They are poor at making deterministic numeric decisions without hallucination. Put the safe and auditable numeric logic in deterministic code and use the model for narrative and adaptation.
-
Favor open data where it’s available
- Open-Meteo, Overpass and BRouter could all be self-hosted. That reduces vendor lock-in and operational surprises.
-
Keep the interaction micro-fast
- The app’s value is time saved. Aim for minimal taps and clear outputs. Stream the plan to reduce perceived wait times.
-
Don’t hide uncertainty
- Mark estimated gusts, show the age of a cached forecast, and explain why a score was assigned. Users trust a tool that surfaces uncertainty.
-
Make offline first for outdoor tools
- Provide GPX downloads and local caches. Many users leave the phone in a pocket and rely on a watch.
These principles apply widely beyond this project to any small, safety-sensitive application that combines data and language models.
How to try it and what you need
The code and instructions live at git.b0b.be/bdeb/workout-suggester. Requirements to run the prototype:
- A machine capable of running a small LLM server or access to an OpenAI-compatible model endpoint (oMLX, llama.cpp, Ollama, LM Studio).
- Go toolchain to build if you prefer building locally, but the repository ships a compact binary in the demo.
- Optional BRouter instance or BRouter online for routing generation.
- A browser and a modern watch or bike computer that accepts GPX files.
Steps:
- Clone the repo and copy .env.example to .env.
- Set LLM_BASE_URL and other endpoints in .env to match your local or remote services.
- make run and open http://127.0.0.1:3000.
- Tap “Here”, pick activity/intensity/time and review the results.
- Download the GPX and upload to your watch if you want offline navigation.
The app is intentionally modular: advanced users can replace each service with a self-hosted instance by changing a single environment variable.
Where the project sits in the broader landscape
This project demonstrates a practical approach to building small, trustworthy applications that combine open data and local models. It sits at the intersection of these trends:
- Local inference: users and small teams increasingly prefer models they can run privately on local hardware or on a controlled server.
- Open geospatial and weather data: volunteer-driven maps and open-weather projects enable niche, local-first applications that closed providers don’t support.
- Responsible LLM use: assigning deterministic responsibilities to code and reserving language models for explanation and adaptability reduces hallucination risk.
Large cloud chatbots offer smoother conversational polish, but they come with recurring costs, privacy trade-offs and limited control over data sources. For an application whose main value is quick, repeatable daily decisions tied to local geography and weather, the small, auditable, open alternative is often the better fit.
FAQ
Q: What model does workout-suggester use? A: The prototype uses Gemma 4 E2B (gemma-4-E2B-it-qat-4bit) running locally via oMLX, but any OpenAI-compatible model server works. You can swap models with an environment variable (LLM_MODEL). The architecture is model-agnostic; the critical part is keeping deterministic rules in Go and using the model for narrative output.
Q: How does the app decide whether it’s safe to go outside? A: Safety decisions and numeric scoring are performed by deterministic Go code. The app fetches open weather data, computes gusts and wind direction, and applies weighted penalties to each activity. Thunder leads to a flat zero score. If all activities fall under a configured threshold (e.g., 40), the app recommends an indoor HIIT session. The model is used to explain the choice and craft the workout, not to make safety determinations.
Q: What weather data sources does it use? A: Primary: Open-Meteo (no key). Secondary fallback: MET Norway. Sunrise/sunset are computed locally. If gust measurements are missing from the primary source, gusts are estimated conservatively and visibly marked as approximations.
Q: Can I run everything locally? A: Yes. Open-Meteo, Overpass, BRouter and your LLM server can all be self-hosted. Each endpoint is configurable via environment variables. Self-hosting avoids per-query bills and gives you control over failures and updates.
Q: Will it work where I live? A: The app is designed for regions with decent OpenStreetMap coverage and public weather forecasts. It works best where signposted routes and knooppunt networks are present (e.g., parts of Europe). In regions with sparse OSM mapping, route suggestions will still work but may lack signposted alternatives.
Q: How does the wind-aware routing work? A: The app computes wind direction and generates round-trip routes that intentionally start into the wind so returning tends to be with a tailwind. BRouter produces candidate loops, and the app annotates them with wind exposure, climb and paved percentage. It filters out implausible long-distance results and scales routes to match the requested distance.
Q: Can I export GPX for my watch? A: Yes. Every route can be exported as GPX. The app is built so you can download the GPX to your device before leaving and navigate the route offline.
Q: Does the app store my workouts in the cloud? A: No. The design favors local operation. Caches (routes and Overpass results) are stored locally for a week to support offline usage. If you wish to integrate cloud services (Strava, etc.) that is a possible extension but not part of the default privacy-preserving approach.
Q: How customizable are the scoring rules? A: Scoring rules are implemented in Go and can be inspected, adjusted and extended. Penalty weights for gusts, rain and darkness are straightforward to modify if you want different risk tolerances.
Q: What are the known limitations? A: Limitations include local weather model granularity, OSM data completeness depending on region, resource needs for local model inference, and BRouter’s occasional round-trip distance quirks that require scaling. The app is not a substitute for official safety advisories.
Q: How can I contribute or extend the project? A: The repository (git.b0b.be/bdeb/workout-suggester) is set up for contributions. Useful extensions include heart-rate/power integrations, Strava sync, improved microclimate inputs, more advanced routing preferences, and enriched safety heuristics. The modular architecture makes it straightforward to add features.
Q: Is the app free to run? A: Yes, if you self-host open dependencies and a free model server. Running larger cloud models may incur costs, and some self-hosted servers require hardware capable of model inference. The core idea is to let you run daily queries without per-token bills.
Q: Can the app handle group rides or multi-start points? A: Current prototype centers on single-start sessions. Adapting it for group planning or multi-start coordination is feasible and a natural extension that would require combining multiple start points and group-level scoring.
Q: Where can I see the code and try a demo? A: The code is available at git.b0b.be/bdeb/workout-suggester. The README includes a quick start. The demo requires pointing the app to an OpenAI-compatible model server.
Try it, inspect the rules, and consider how a small, deterministic core plus a focused local model can reduce friction for routine decisions. The result is a usable tool that gets more people moving without requiring a cloud subscription or a lengthy setup.