KBL
오늘의 경기·부문별 1~5위·선수 카드·리더보드·역대 기록까지, 1997년부터 모든 KBL 선수 기록을 한눈에. 비영리 팬 프로젝트.
Just Watts는 실내 사이클링용 무료 오픈소스 스마트 트레이너 앱입니다
Just Watts is a free, open-source smart trainer app for indoor cycling: ERG workouts, real-road courses, ride history and uploads to Garmin and Strava.
스포츠·피트니스JavaScript · PythonGitHub @yaportmax온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Free, open-source smart trainer app: ERG workouts, real-road courses, open world, ride history, Garmin/Strava/TrainingPeaks/intervals.icu sync
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A free, open-source smart-trainer app.
Just Watts connects to any Bluetooth FTMS smart trainer and heart rate strap, runs your workouts in ERG mode or lets you ride free by grade, watts or resistance level, and keeps your history: NP, TSS, zones, intervals, trends. It runs on your own computer, with no account and no API keys. The trainer connection lives in a small local server, so reloading or closing the browser never drops the trainer or the ride.
Try it in the browser, no install: justwatts.com (Chrome or Edge, Web Bluetooth; rides stay in the browser, no uploads). The local app below adds Garmin and Strava upload and a trainer connection that survives closing the tab.
Needs Python 3.10+ and a computer with Bluetooth LE. Any modern browser works for the page.
git clone https://github.com/yaportmax/justwatts
cd justwatts
pip install -e .
justwatts serve
Open http://127.0.0.1:8765, press Connect trainer,
pick it from the scan list. The server remembers it and reconnects by itself. A trainer accepts one
controller at a time: close Zwift or any other app using it first. start.cmd (Windows) or
./start.sh (macOS, Linux) starts the server and opens the browser.
No trainer? justwatts simulate starts a workout on the server's fake trainer; watch it in the page.
10m 45-75%, 5x(3m 115%, 3m 50%), 10m free),
.zwo import and export, an FTP ramp test.app/roads/marin, app/world) so a ride never waits on a server.web/api/_lib/social.js), no extra service. Visibility can be switched
off in Settings. The local app has no accounts, so none of this shows there.Common actions sit on the ride screen; the rest are in Adjust. Each change shows what changed, and intensity, watts, segment time, length, trainer mode and settings can be undone for 15 s.
| Control | Where | Key |
|---|---|---|
| Pause, resume (Pause during an auto-pause stays paused) | bar | Space |
| Intensity ±1 % (grade, watts or level when not in ERG) | bar −/+ | ↑ ↓ (Shift ±5) |
| Back (restart segment; in its first 10 s, the previous one), Skip | bar | ← → |
| Segment time ±1 min (±30 s in Adjust) | bar | [ ] |
| Segment watts ±5/±10 | Adjust | - = |
| Intensity presets, reset to 100 % | Adjust | |
| Workout length ±5 min | Adjust | |
| Trainer: ERG, or ERG off by grade or level (the plan keeps showing) | Adjust | 1 2 3 |
| Edit the plan from the current segment | Adjust | |
| After the plan: hold the last target or ride flat | Adjust | |
| Cadence on/off and range (off: no readout, target or cues), beeps | Adjust | |
| Map style (Map, Satellite, Topo, Cycling), 3D terrain, map or chart focus (courses with a route) | map | F |
| Open world: the way at the junction ahead (shown about 200 m before it) | tap | ← ↑ → |
| Open world: U-turn (ride back the way you came) | bar, junction prompt | ↓ |
| Open world: go to another spot, the ride goes on | bar (Spots) | P |
| Full screen | bar | Shift F |
| Undo, Save, End (two taps) | bar | Z, S |
| Reconnect trainer or HR | header |
? shows the keys. Free segments and a flat finish ride by grade, and −/+ moves it. FTP tests keep
their targets.
| Device | Support |
|---|---|
Smart trainers with Bluetooth FTMS (service 0x1826) | ERG, grade simulation, resistance level |
| Tested | Zwift Hub (server Bluetooth, survives page reloads) |
HR straps, Bluetooth Heart Rate service 0x180D | Yes; trainer-relayed HR is used if no strap. Straps that hide the HR service until they feel a heartbeat (seen on a Wahoo TRACKR) show "waiting for heartbeat" and connect once you wet the contacts. |
| ANT+ only devices | No |
Most current trainers (Wahoo, Tacx, Elite, Saris, JetBlack) expose FTMS; only the Zwift Hub has been tested so far. Resistance-level mode follows FTMS 1.0 and is untested on hardware.
Garmin (pip install "justwatts[garmin]", then justwatts login-garmin once). On first start with
Garmin linked, Just Watts imports your FTP and weight (only the ones you have not set yourself;
Import from Garmin in Settings redoes it and also brings HR zones). HR zones from Garmin are a
choice under Settings > HR zones; the default is % of max HR. It uses the unofficial
python-garminconnect library, which Garmin can
break at any time. Garmin is only active for the default data dir; JUSTWATTS_GARMIN=off or
serve --no-garmin disables it, and any other JUSTWATTS_DATA has it off unless JUSTWATTS_GARMIN=on.
Strava (no extra needed). Press Connect Strava in Settings and approve on Strava. This uses
Just Watts's Strava app: its client secret is not in this repo, it lives only in a tiny stateless token
service (web/api/strava/token.js) that swaps the sign-in code and refresh
tokens for access tokens. Uploads go straight from your computer to Strava; tokens stay in your data
folder. To skip the token service, open Use your own Strava API app in Settings and enter your own
Client ID and secret (strava.com/settings/api, callback domain localhost), or host the function
yourself and set JUSTWATTS_STRAVA_RELAY and JUSTWATTS_STRAVA_CLIENT_ID. Uploads go through the official API with the FIT file,
the ride name and a text summary (NP, IF, TSS, intervals, "Recorded with Just Watts"). If Garmin
Connect already syncs to Strava for you, uploading to both makes duplicates: pick one.
Share card: the ride summary renders a PNG card to download or share. Strava's API cannot attach photos, so add it by hand. If you set a public base URL in Settings (your own proxy or host), the Strava description links the card.
Every FIT file is saved to ~/.justwatts/rides/ before any upload, and History can always export
.fit and .tcx.
The server binds 127.0.0.1 and rejects any non-localhost Host header (the only exception is
/share/<ride>.png when you set a public base URL). The page proves itself with its own Origin
plus an X-HW: 1 header; the CLI uses a bearer token from a 0600 file. Details in
docs/PROTOCOL.md.
| Env | Default | |
|---|---|---|
JUSTWATTS_PORT | 8765 | server port |
JUSTWATTS_DATA | ~/.justwatts | settings, devices, rides, the ride in progress, logs, token, Strava tokens |
JUSTWATTS_GARMIN_TOKENS | ~/.garminconnect | Garmin session tokens |
JUSTWATTS_TOKEN | from <data>/token | API token for the CLI |
JUSTWATTS_GARMIN | auto | off disables Garmin, on forces it for a non-default data dir |
JUSTWATTS_BLE | on | off disables Bluetooth |
JUSTWATTS_STRAVA_RELAY | justwatts.com | Strava token service for the shared app; off allows only your own API app |
JUSTWATTS_STRAVA_CLIENT_ID | 168528 | Strava client ID used with that token service |
Older OPENSPIN_* and HW_* names still work. An existing ~/.openspin data dir keeps being used,
and older data dirs move to ~/.justwatts on first start.
pip install -e ".[dev]"
pytest
Tests run the browser code under Node (node 18+ on PATH), boot the full page against a real test
server, drive BLE through a fake bleak module, mock Garmin and Strava, force Garmin and Bluetooth
off, and check that a full ride works with none of the optional extras installed.
See SECURITY.md for the threat model and how to report a vulnerability.
MIT, see LICENSE. The vendored MapLibre GL JS 5.24.0 (app/vendor/, the 3D map) is BSD-3-Clause,
license next to it.