Initial: Mesh-Configs vom 03.09.2026 01:23

This commit is contained in:
root
2026-09-03 01:23:55 +02:00
commit 4b65134f8c
797 changed files with 146197 additions and 0 deletions
@@ -0,0 +1,95 @@
# COROS-MCP — Historie/Backfill + Parser-Pitfalls (verifiziert 2026-08-18)
Ergänzt zu `coros-mcp-data-shapes.md`. Alle Befunde sind reale `call-tool`-Antworten, nicht geraten.
## Health-Historie: days-Parameter + Server-Limit
`queryDailyHealthData` akzeptiert im Schema einen `days`-Parameter (integer, default 7).
```bash
coros-mcp call-tool --tool queryDailyHealthData --arguments-json '{"days":30}'
coros-mcp call-tool --tool queryDailyHealthData --arguments-json '{"days":365}'
```
**WICHTIG (verifiziert 2026-08-18):** Egal ob `days:30`, `days:90` oder `days:365` — die COROS-API liefert **maximal ~53 Health-Tage** zurück (z.B. nur `2026-05-16 .. 2026-08-15`, 53 Blöcke). Das ist ein **SERVER-Limit von COROS**, kein Parser- oder Client-Fehler. Mehr Health-Historie gibt es über diese API nicht; für längere Geschichte bleibt nur der Sport/FIT-Pfad.
Auch bei "alles was geht" lohnt kein Versuch über 53 hinaus zu holen — wiederhole `sportBackfill` dafür nicht.
## Sport-Historie: querySportRecords mit Datumsbereich
`querySportRecords` akzeptiert (anders als Health) **`startDate`/`endDate` im Format `yyyyMMdd`** (kein `days`) und `sportTypeCodes`-Array:
```bash
coros-mcp call-tool --tool querySportRecords --arguments-json \
'{"startDate":"20250501","endDate":"20260815","sportTypeCodes":[65535]}'
```
- `sportTypeCodes: [65535]` = alle Sportarten (sonst numerische Codes, nie Namen).
- **Sport-Historie reicht deutlich weiter zurück als Health** (1 Jahr+). 20 Records über 15 Monate bestätigt.
- Antwort enthält je Workout: `N. <Sportart> — YYYY-MM-DD`, `Time Window: startTimestamp=.. | endTimestamp=..`, `Duration`, `Avg HR: <n> bpm`, `Calories: <n> kcal`, `Distance: <x> km`, `LabelId: <Zahl>`, `SportType: <code>`.
## 🔥 PITFALL: COROS-`content[0].text` enthält LITERALES `\n`, KEINE echten Newlines
(Wichtigster Fund — ohne Normalisierung matcht **nichts**.)
`querySportRecords` (und teils andere) liefern im Text **literale 2-Zeichen-Sequenz `\n` (ein Backslash + n)**, keine echten Zeilenumbrüche. `splitlines()` / `repr()` in einem Launch zeigen das nicht zuverlässig.
**Debug-Befund (2026-08-18):**
```python
text = d['content'][0]['text']
'\n' in text # False (kein echter Newline)
'\\n' in text # True (literales Backslash-n vorhanden)
```
**Fix — normalisiere VOR jedem Zeilen-Parsing:**
```python
text = text.replace('\\n', '\n')
lines = text.splitlines() # erst jetzt funktioniert splitlines()
```
Übersehen → alle Regex gegen `splitlines()` finden 0 Zeilen (kein Fehler, nur 0 Treffer). Achte auf die richtige Backslash-Tiefe: Ersetze das **1-Backslash-`\n`-Literal**, nicht doppel.
## ⏱️ Dauer-Format ist uneinheitlich: HH:MM:SS vs MM:SS vs SS
COROS-`Duration:`-Werte kommen **gemischt**:
- `1:00:14` → 1h = **60 min** (3 Segmente)
- `2:03:36` → 2h03m = **123 min** (3 Segmente)
- `51:35` → **51 min** (2 Segmente — NICHT 51std35!)
- `0:22` → 22 Sek → 0 min (2 Segmente)
**Fehlerhafte Regex** `(\d+):(\d+)(?::(\d+))?` macht `51:35` fälschlich zu `51*60+35 = 3095 min` (absurd 63h). **Robust:**
```python
m = re.search(r'Duration:\s*([\d:]+)', block)
seg = [int(x) for x in m.group(1).split(':')]
if len(seg) == 3: # HH:MM:SS
dauer_min = seg[0]*60 + seg[1]
elif len(seg) == 2: # MM:SS
dauer_min = seg[0]
else: # SS
dauer_min = seg[0] // 60
```
## Sport-Daten → SEPARATE Tabelle (nicht health_records)
Ein Tag kann **mehrere Workouts** haben → passt NICHT in `health_records` (1 Zeile/Tag). Eigene Tabelle:
```sql
CREATE TABLE IF NOT EXISTS sport_records (
label_id TEXT PRIMARY KEY, -- COROS-LabelId je Workout (natürlicher PK)
datum TEXT NOT NULL, -- yyyyMMdd
sportart TEXT NOT NULL,
sport_type_code INTEGER, -- COROS SportType-Code
dauer_min INTEGER,
avg_hr INTEGER,
kalorien INTEGER,
distanz_km REAL,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_sport_datum ON sport_records(datum);
```
**Idempotent:** `INSERT OR IGNORE ... ON CONFLICT(label_id) DO NOTHING` — Workouts sind nach Import unveränderlich, kein UPDATE nötig. (Direkter DB-Write, da `health_records`-API createRule leer = nur Superuser schreibt; siehe `pocketbase-deployment`.)
## Reihenfolge bei Backfill in bestehende DB
1. Wenn schon fehlerhafte Records mit dem alten Dauer-Parser geschrieben wurden → Flushing die Tabelle (`DELETE FROM sport_records`) oder per label_id neu überschreiben, DANN Neu-Load mit korrektem Parser.
2. Immer vorher DB-Backup (Backup-Regel): `cp /opt/pocketbase/pb_data/data.db data.db.bak-<ts>`.