OpenAthlete
AGPLv3 endurance training platform: EU-oriented hosting, transparent CTL/ATL/TSB logic in code, self-hosting, and full export. Built in Grenoble.
Smarttrainer verbinden, Watt wählen, treten. Web-Bluetooth-PWA: ERG-Modus, Programme, Auswertung — alles lokal, kein Konto.
Translation pending
Sports & fitnessJavaScript · CSSGitHub @grenzenloseSchubladeOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Wahoo KICKR als Ergometer im Browser — wattgesteuertes Training ohne Zwift, ohne Abo, ohne Konto. Web-Bluetooth-PWA (FTMS).
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Zwei Taps bis zum Treten. ergoMergo macht aus einem Wahoo KICKR (oder jedem FTMS-Smarttrainer) ein wattgesteuertes Ergometer — direkt im Browser, ohne Zwift, ohne Abo, ohne Konto, ohne virtuelle Welt. Trainer per Bluetooth verbinden, Programm oder Wattzahl wählen, fahren.Read the full README ↗
Zwei Taps bis zum Treten. ergoMergo macht aus einem Wahoo KICKR (oder jedem FTMS-Smarttrainer) ein wattgesteuertes Ergometer — direkt im Browser, ohne Zwift, ohne Abo, ohne Konto, ohne virtuelle Welt. Trainer per Bluetooth verbinden, Programm oder Wattzahl wählen, fahren.
Live: https://grenzenloseschublade.github.io/ergoMergo/
Die App schickt deine Daten nirgendwohin — kein Server von uns, kein Konto, keine Analyse, keine Cookies, keine fremden Bibliotheken. Fahrten (inklusive Herzfrequenz), Einstellungen und Programme liegen nur im Browser auf deinem Gerät. Eine Content-Security-Policy lässt den Browser Verbindungen nur zur eigenen Adresse der App zu — selbst eingeschleuster Code könnte nichts an einen fremden Server senden.
Ohne Netz läuft alles. Ist das Gerät online, fragt die App nur sw.js ab, um
Updates zu erkennen (beim Öffnen, beim Zurückkehren, alle 10 min — nie während
der Fahrt); GitHub als Hoster sieht dabei wie bei jedem Seitenaufruf IP-Adresse
und Zeitpunkt. Sicherung und TCX-Export entstehen nur auf deinen Tipp als Datei
auf deinem Gerät.
| Datenschutz-Etikett | |
|---|---|
| Vom Entwickler erhoben | nichts |
| An Dritte weitergegeben | nichts |
| Tracker / Werbung | keine |
| Berechtigungen | Bluetooth, nur für die im Chrome-Dialog gewählten Geräte |
| Speicherort | nur dein Browser (IndexedDB) |
| Löschen | jederzeit in der App bzw. über die Browserdaten |
Belegt durch eine automatische Selbstprüfung vor jeder Veröffentlichung — Prüfkatalog, Speicher-Inventar, Grenzen und „So prüfst du es selbst": docs/sicherheit.md. Funde bitte über SECURITY.md melden.
Wer nur strukturiert Watt treten will, braucht keine 3D-Welt, kein Monatsabo und keinen Account. Was fehlte: eine App, die den Trainer ansteuert wie ein Studio-Ergometer — Ziel-Watt, Intervalle, ±-Tasten am Lenker, Auswertung. ergoMergo ist genau das: eine einzige statische Web-App (Vanilla JS, Web Bluetooth/FTMS, keine Buildkette, kein Backend), alle Daten bleiben lokal auf dem Gerät.
| Bereich | Was drin ist |
|---|---|
| ERG-Steuerung | Ziel-Watt mit sanften Rampen, ±-Tasten (Touch, Tastatur, Zwift Click/Ride), Not-Stopp mit geschütztem WEITER |
| Trainingsplan | Tage/Dauer/Ziel wählen, Wochen mit Aufbau und leichter Testwoche, heutige Einheit mit einem Tipp, verschieben/kürzer/leichter/auslassen mit Rückgängig |
| Programme | Zeitbasierte Generatoren (Sprint 30/30, HIIT 40/20, VO2max 4×4, Schwelle, Ausdauer, Recovery), klassische Programme, FTP-Rampentest, .zwo-Import (Einstellungen → Erweitert, standardmäßig aus) |
| Fahrbildschirm | Live-Graph mit Programmprofil und Positionscursor, Blocksteuerung (überspringen/zurück/+30 s), Kadenz-Zielbereiche, Fokus-Modus (Tap auf Werte oder Graph kippt die Gewichtung), LED-Statuszeile, Gerätezustand oben links, Bild-in-Bild (Experiment) |
| Ansagen & Töne | Countdown vor jedem Wechsel, unterscheidbare Signale für härter/leichter (Dur-Dreiklang rauf bzw. Töne abwärts), Sprachansagen aus vorgerenderten Audio-Bausteinen — funktionieren auch mit Bildschirm aus |
| Geräte | Trainer, Herzfrequenz-Gurt, Zwift Click/Ride; Tasten frei belegbar per Lern-Modus; Schnellverbindung ohne Geräteauswahl (mit Chrome-Flag) |
| Auswertung | Dauer, Ø/max/NP, IF/TSS, HF, Distanz (aus Trainer-Speed integriert, im ERG-Modus gangabhängig), Zeit in Zonen, Soll/Ist-Vergleich pro Block, Wochenbilanz, TCX-Export, Akkuverbrauch pro Fahrt |
| Plattform | Installierbare PWA, offlinefähig, alles lokal in IndexedDB — kein Server, keine Cloud; Demo-Modus ohne Trainer (Button auf dem Startbildschirm) |
css/tokens.css; lebendiger Style Guide
(docs/stil.html),
eine Prüfung verhindert feste Werte. Alle Tippflächen ≥ 44 px..zwo-Import ist jetzt eine Einstellung (Erweitert), standardmäßig aus.npm test prüft Stil, Plan-Regeln (alle
Wochentag-Kombinationen, Verpass-Muster, Verschiebungen), Abläufe und
Layout.| Gerät | Firmware/Version |
|---|---|
| Wahoo KICKR CORE 2 | FW 3.5.37 |
| Zwift Ride (Lenker) | FC82-Firmware (ab Jan 2025), Lern-Modus für Tastenbelegung |
| Samsung-Smartphone, Android-Chrome | Primärziel (Handy am Lenker) |
| Ubuntu-Laptop, Chrome | Sekundärziel |
Jeder FTMS-Trainer sollte funktionieren (Standard-Protokoll); getestet ist die Kombination oben. iOS wird nicht unterstützt (kein Web Bluetooth in Safari).
chrome://flags/#enable-web-bluetooth
aktivieren. Scheitert das Pairing, den Trainer einmalig per bluetoothctl
koppeln (pair + trust).chrome://flags/#enable-web-bluetooth-new-permissions-backend
erspart die Geräteauswahl bei jedem Start (Schnellverbindung).Dauer und Trainingstyp wählen — die App generiert ein vollständiges Programm mit Warmup-Rampe, skaliertem Hauptteil und Cooldown. Intensitäten sind fix (%FTP), die Zeit skaliert über Wiederholungen und Z2-Füller. Ohne FTP-Wert wird 170 W angenommen. Kein Tabata, keine 10-s-Sprints: ERG braucht 2–6 s pro Sollwertwechsel, kürzere Intervalle wären überwiegend Rampe. Strukturen, Formeln und Quellen: docs/programme.md
Alle Daten liegen lokal in einer IndexedDB (ergomergo) im Chrome-Profil —
kein Server, kein Konto, keine Cloud. Stores: sessions (Kennwerte je Fahrt),
sessionData (Rohsamples 1/s), settings, programme, logs (Diagnose).
navigator.storage.persist() schützt vor automatischem Aufräumen. Sicherung:
Komplett-Backup als JSON (Einstellungen → Sicherung) und TCX-Export je Fahrt.
Einmal verbundene Geräte (Trainer, HF-Gurt, Zwift Click/Ride) werden gemerkt; mit persistenten Web-Bluetooth-Berechtigungen verbindet die App beim Start ohne Geräteauswahl, HF-Gurt und Controller automatisch mit. Zwift-Ride-Tasten werden in Einstellungen → Geräte → „Tasten zuordnen" interaktiv belegt (+/−, Block vor/zurück, STOPP), je Aktion auch mehrere Tasten; ist von einem Paddle nur eine Richtung belegt, wirkt es in beide. Ab Werk: Watt hoch = beide Paddles nach außen, Watt runter = beide nach innen, Block vor/zurück = Pfeil rechts/links, STOPP = B. Die rpm-Anzeige kommt direkt vom Trainer (der KICKR schätzt die Trittfrequenz selbst, kein externer Sensor nötig).
Die App liest die Trainer-Firmware aus (Geräteliste, Diagnose-Log). Updates
macht sie nicht — es gibt kein öffentliches Protokoll: der KICKR CORE 2
aktualisiert über WLAN/Wahoo-App, Zwift Ride/Click über die Companion-App.
Ride-Firmware ab Jan 2025 wechselt die BLE-Service-UUID auf FC82 — die App
unterstützt beide Varianten. Fehlersuche: docs/debugging.md
python3 -m http.server 8000
# http://localhost:8000 — localhost ist Secure Context, HTTPS lokal nicht nötig.
Dev-Parameter: ?demo=<workoutId> (z. B. ?demo=vo2max — Demo-Fahrt ohne
Trainer), ?demo (freies Fahren); alle Parameter in
docs/debugging.md. Test vom Android-Gerät:
GitHub-Pages-Deployment oder USB-Debugging + chrome://inspect.
Farben, Schriftgrößen, Radien und Abstände kommen nur aus css/tokens.css
(Regeln: docs/stil.md, alle Bausteine live:
docs/stil.html).
node tools/stil-check.mjs # feste Werte außerhalb der Tokens (läuft auch in release.mjs)
node tools/datenschutz-check.mjs # jede Netzstelle freigegeben, CSP unverändert (läuft auch in release.mjs)
node test/sicherheit.mjs # präparierte Sicherung: kein Schadcode, bereinigt
node test/netz.mjs # Netz-Mitschnitt aller Abläufe inkl. Service Worker, CSP, Speicher
node test/layout.mjs # Fahrbildschirm in 5 Größen: Bedienleiste, Überlauf, Zentrierung, Sprünge, Panel
node test/ablauf.mjs # Navigation, Einstellungen, Fahrt, Programm, Demo-Wege mit Sollwerten
Die Browser-Tests brauchen Node ≥ 22 und ein Chrome (CHROME=/pfad/zu/chrome,
sonst Playwrights chrome-headless-shell oder google-chrome); sie starten
ihren eigenen Server.
Der Versionsstempel steht doppelt (VERSION in sw.js für die
Update-Erkennung, APP_VERSION in js/version.js für den
Install-Konsistenz-Check) — laufen sie auseinander, bricht jede
Service-Worker-Installation ab. Deshalb ausschließlich über das Skript bumpen:
node tools/release.mjs # nächste Version, setzt beide Stempel
node tools/release.mjs --check # prüft vor dem Push auf Drift
Danach committen (Konvention: „…; Release vXX") und pushen. Der Workflow
.github/workflows/pages.yml prüft (Stil, Datenschutz, Sicherheit, Netz) und
veröffentlicht nur die App-Dateien aus tools/auslieferung.mjs; installierte
Apps bieten das Update per Button an.
tools/log-pull.mjs holt das Diagnose-Log und die Fahrtdaten direkt aus der
IndexedDB der App auf dem Android-Gerät — ohne Export in der App. Es nutzt
adb forward auf den Chrome-DevTools-Socket und das DevTools-Protokoll.
Voraussetzungen: USB-Debugging, Chrome mit offenem ergoMergo-Tab, Node ≥ 22.
node tools/log-pull.mjs # Log anzeigen + Komplett-Dump als JSON
node tools/log-pull.mjs --logs-only # nur das Log
node tools/log-pull.mjs --seit 2026-09-21 # Log ab Datum
MIT