Legt geplante Einheiten im Kalender an — die Grundlage fuer jeden Soll-Ist-Vergleich.
Mehrere Einheiten in einem Aufruf (bis 28, also vier Wochen). Der Aufruf ist idempotent: existiert am
selben Tag bereits eine Einheit derselben Sportart mit demselben Titel, wird sie uebersprungen statt
verdoppelt — ein Wiederholungsversuch nach einem Timeout ist also gefahrlos.
Die angelegten Einheiten werden vom Import automatisch mit passenden Aktivitaeten verknuepft; danach
zeigt training_stats mit include_planned den Abgleich.
**Die Intervallstruktur gehoert in steps, nicht in notes.** Nur Schritte landen auf der Uhr: der
Garmin-Versand baut daraus Aufwaermen, Intervalle, Erholung und Auslaufen mit eigenen Zielwerten.
Ohne Schritte wird daraus ein einziger Block ueber die Gesamtdauer, und der Athlet sieht unterwegs
nur eine Zahl. notes ist fuer Hinweise an den Athleten -- Absicht der Einheit, Gefuehl, Technik.
Je Schritt **entweder** Dauer **oder** Distanz -- beides zusammen ist auf der Uhr mehrdeutig, sie
beendet den Schritt beim ersten erreichten Kriterium.
**Ohne Dauer und ohne Distanz laeuft der Schritt bis zur Rundentaste.** Das ist kein fehlender
Wert, sondern die saubere Form fuer Pausen und Technikbloecke: der Athlet druckt, wenn er soweit
ist. Fuer kurze Pausen mit fester Laenge gibt es duration_sec (Sekunden), weil duration_min ganze
Minuten sind und 10-30 s im Schwimmen der Normalfall.
**Beim Schwimmen distance_m statt distance_km** -- distance_km: 0.05 fuer ein 50er ist einen Nulldreher vom
500er entfernt. Und **stroke/equipment gehoeren als Felder mit, nicht in notes**: nur so steht auf
der Uhr "Drill" oder "Paddles" und nicht bloss "100 m". Beides wirkt nur bei sport: SWIM.
**Ziele sind Spannen, keine Punkte** (target.low / target.high). Garmin verlangt ohnehin ein
Fenster und hat es sich bisher aus dem Einzelwert erfunden. Bei pace ist low der *schnellere* Wert;
numerisch gilt immer low <= high.
**Relativ planen ist der bessere Weg**: ftp_pct, lthr_pct und threshold_pace_pct werden **beim
Anlegen** gegen die Schwellenwerte aus training_profile aufgeloest und **absolut** gespeichert. Ein
spaeterer FTP-Test verschiebt damit keine alte Planung rueckwirkend; was gewuenscht war und wogegen
aufgeloest wurde, bleibt in target_source nachlesbar. Fehlt der Schwellenwert, bleibt das Ziel leer
und die Antwort sagt es — geraten wird nichts.
**threshold_pace_pct ist Prozent der Schwellengeschwindigkeit, nicht der Pace** — mehr Prozent ist
**schneller**. Langsamer als die Schwelle sind damit Werte **unter** 100, nicht darueber. Wer die
Skala als Zeit pro Kilometer liest, schreibt 130-148 fuer eine lockere Einheit und bekommt bei einer
Schwelle von 3:46/km 2:32-2:54/km — das ist der haeufigste Fehler an diesem Feld. Richtwerte Laufen:
Trabpause / Regeneration 62-72 Tempodauerlauf 88-94
Ein- und Auslaufen 70-80 Schwelle 98-102
GA1 / lockerer Dauerlauf 72-80 kurze Reize (30-60 s) 105-112
Angenommen werden nur 60-120; alles darueber weist der Server ab. **Nach dem Anlegen gegenlesen:**
der erzeugte Titel nennt die aufgeloeste Pace ("40 min @ 4:42-5:13/km"). Steht dort etwas um 2:30-3:00
fuer eine lockere Einheit, ist die Skala verdreht.
Beim Schwimmen loest es gegen die CSS auf, **wenn** eine im Profil steht (training_profile,
swim_css_pace_sec_per_100m) — sie wird nicht berechnet, sondern vom Athleten gesetzt. Fehlt sie,
faellt das Ziel aus und die Antwort sagt es.
**Wiederholungen als Block**: { repeat: 8, label: "400er", steps: [...] } statt achtmal derselbe
Schritt — "3 x (1 km zuegig + 1 min Pause)" ist ein Eintrag, kein sechsfacher. training_plan_list
gibt sie in derselben Form zurueck ("3 x:" mit den Schritten darunter), obwohl sie flach
gespeichert werden. Der Server rollt sie aus und nummeriert "(1/8)"; auf der Uhr entsteht daraus
wieder eine echte Wiederholungsgruppe, also "Intervall 2 von 8" statt "Schritt 5 von 20". Ein Block darf einen Block enthalten --
**zwei Ebenen**, also 2 x (4 x 50 + 100 locker), wie Schwimmsaetze geschrieben werden. Tiefer geht
es nicht; auf der Uhr steht ohnehin eine flache Liste.
Args:
- sessions (array, 1-28):
- date (string): YYYY-MM-DD
- sport (string): SWIM | BIKE | RUN | LONGRUN | STRENGTH | MOBILITY | YOGA | HIIT | CARDIO | WALK | HIKE | ALPINE_SKI | NORDIC_SKI | SOCCER | RACKET | OTHER
- title (string): 1-120 Zeichen
- start_time (string, optional): "HH:MM" in der **Ortszeit des Athleten** (die Zone steht in
training_profile, time_zone). Schliesst time_of_day aus.
- time_of_day (string, optional): MORNING | AFTERNOON | EVENING — der Tag steht, die Uhrzeit
noch nicht. Schliesst start_time aus.
- duration_min (number, optional)
- distance_km (number, optional)
- intensity (string, default EASY): EASY | MODERATE | HARD | VERY_HARD
- notes (string, optional): Markdown, Hinweise an den Athleten (NICHT die Struktur)
- pool_length_m (number, 10-100, optional, nur SWIM): Beckenlänge in Metern. Geht mit
training_plan_push_garmin auf die Uhr, damit sie die Bahnen richtig zählt; fehlt sie,
nimmt die Uhr ihre eigene Einstellung. Das übliche Becken des Athleten steht in
training_get einer früheren Schwimmeinheit (Abschnitt "Becken").
- brick (boolean, optional): Koppeleinheit, verknuepft mit der Radeinheit desselben Tages
(der zuletzt beginnenden mit duration_min, auch aus diesem Aufruf; dort zuerst das Rad
auffuehren). Hat das Rad eine feste Uhrzeit, beginnt der Lauf an dessen Ende; steht es in
einem Tagesfach, steht der Lauf im selben Fach direkt dahinter. Verschiebt
training_plan_update das Rad, wandert der Lauf mit. Schliesst start_time und time_of_day
aus. Ohne brick meldet die Ueberschneidungspruefung Rad und Lauf als Konflikt. Gibt es kein
Rad, wird die Einheit uebersprungen.
- steps (array, optional): die Intervallstruktur. Ein Eintrag ist entweder ein Schritt
oder ein Wiederholungsblock; nach dem Ausrollen hoechstens 120 Schritte.
Schritt:
- type (string, default WORK): WARM_UP | WORK | RECOVERY | COOL_DOWN | REST | CUSTOM
- title (string, optional): 1-120 Zeichen. Fehlt er, wird er aus Groesse und Ziel gebaut.
- label (string, optional): "Sweet Spot", "locker" — Ersatztitel, wenn title fehlt
- duration_min (integer, optional): **ganze Minuten**
- duration_sec (integer, 5-3600, optional): Sekunden. Schliesst duration_min aus.
- distance_km (number, optional) / distance_m (integer, optional): schliessen einander aus
- stroke (string, optional, nur SWIM): free | backstroke | breaststroke | butterfly |
drill | im | mixed | any | im_by_round
- equipment (string, optional, nur SWIM): fins | kickboard | paddles | pull_buoy | snorkel
- exercise (string, optional, nur STRENGTH): Uebung aus Garmins Katalog, **englisch** wie in
Garmin Connect ("Barbell Back Squat", "Push-up", "Plank") oder als KATEGORIE/UEBUNG.
Ein unbekannter Name wird abgelehnt, die Fehlermeldung nennt passende Katalognamen.
- reps (integer, 1-500, optional, nur STRENGTH): Wiederholungen — der Schritt endet auf der
Uhr danach. Schliesst duration_* und distance_* aus; eine Halteuebung (Plank) nimmt
stattdessen duration_sec.
- weight_kg (number, optional, nur STRENGTH): Zielgewicht
Ein Satzblock ist ein Wiederholungsblock: { repeat: 3, steps: [{ exercise, reps: 10,
weight_kg: 40 }, { type: REST, duration_sec: 90 }] }.
- target (object, optional): { type, low, high }
absolut: power (W) | pace (s/km, Schwimmen s/100m; auch "4:30") | hr (bpm) | zone (1-6) | rpe (1-10)
relativ: ftp_pct (30-200) | lthr_pct (50-130) | threshold_pace_pct (60-120)
Prozent, wird beim Anlegen aufgeloest. ftp_pct und lthr_pct sind intuitiv
(mehr = haerter); threshold_pace_pct ist Prozent der Geschwindigkeit, dort
ist mehr = schneller und "locker" liegt bei 72-80, nicht bei 130.
- target_pace / target_heart_rate / target_power (optional): Kurzform fuer ein Punktziel
- notes (string, optional)
Fehlen Dauer *und* Distanz, endet der Schritt auf Tastendruck (Lap-Taste).
Wiederholungsblock:
- repeat (integer, 2-40): wie oft
- label (string, optional): Name des Blocks, z.B. "400er"
- steps (array, 1-20): Schritte **oder** ein weiterer Wiederholungsblock (zweite Ebene)
Steht am selben Tag schon eine Einheit derselben Sportart mit **anderem** Titel, wird die neue
trotzdem angelegt — zwei Einheiten je Tag sind zulaessig — und die Antwort nennt die bestehende id
unter warnings. War eine Korrektur gemeint, ist training_plan_update der richtige Weg.
**Renntage:** liegt eine HARD/VERY_HARD-Einheit, ein Test ("Test" im Titel) oder eine Einheit über 90 min am Tag eines geplanten
Rennens oder am Tag davor, wird sie trotzdem angelegt — warnings nennt das Rennen. Die Renntage im
Fenster zeigt training_plan_list.
Auf die Uhr kommt der Plan erst mit training_plan_push_garmin.
Returns: { created: [{ id, date, sport, title }], skipped: [{ date, sport, title, reason }], warnings: [...] }
ConnectorOAuth