Oskar Austegard

Indoor Biking Web App — Handoff Document

Purpose

This document is a handoff for a new implementation session to build a web-based indoor biking app. The app connects to a smart trainer over Web Bluetooth, controls resistance based on map grade, reads trainer telemetry (power/cadence/speed), and ingests Zwift Play controller button events (left/right/gear up/down).

Goals (Must Have)

  1. Trainer control loop: Automatically increase/decrease resistance based on map grade.
  2. Zwift Play input: Read button presses (left/right/gear up/down) into the web app.
  3. Telemetry: Read wattage, cadence, and speed/RPM from the trainer into the app UI.

Scope (MVP)

Out of Scope (for MVP)


Key Technical Requirements

1) Trainer Control (BLE FTMS)

Expected control opcodes (FTMS standard):

Constraints:

2) Zwift Play Buttons (BLE)

3) Map + Grade


Data Flow (High Level)

  1. User connects trainer via Web Bluetooth.
  2. User connects Zwift Play via Web Bluetooth.
  3. App loads route and computes grade for the current position.
  4. App sends resistance updates (1–2 Hz) based on grade.
  5. App displays live telemetry from trainer.
  6. Zwift Play buttons update app state (steering UI, gear display, map pan).

Minimal UI Components


Control Algorithm (MVP)

Grade → Resistance Mapping

Update Loop


BLE Constraints & Notes


Testing Strategy (Manual)


Open Questions / Risks

  1. Zwift Play BLE spec: is the notification data publicly documented?
  2. Trainer command support: does target resistance or target power work across all devices?
  3. Grade smoothing: how much filtering is needed to avoid jitter?
  4. Route source: will MVP allow GPX upload or fixed route?

Suggested Implementation Order

  1. Build trainer BLE connection and telemetry display.
  2. Implement route loading + grade calculation.
  3. Add resistance control loop.
  4. Pair Zwift Play and log button events.
  5. Wire button events to UI actions.

Reference Materials


Location for Implementation

This document is stored in:

This repo already has map usage in biking/activity-weather.html which can be used as a template for OpenLayers setup.