Initial: Mesh-Configs vom 03.09.2026 01:23
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
# COROS-MCP Integration (offiziell, Wearable-Daten → PocketBase)
|
||||
|
||||
Muster für das Einspeisen von COROS-Sportuhr-Daten in eine PocketBase-`health_records`-Collection,
|
||||
als Ergänzung zu `pocketbase-deployment/SKILL.md`. Verifiziert 2026-08-15.
|
||||
|
||||
## Install & Login (offizielles Paket, von COROS)
|
||||
|
||||
```bash
|
||||
npm install -g coros-mcp # offizielles Paket (coros_openplatform)
|
||||
coros-mcp --help # commands: login, login-start/-finish/-status, list-tools, describe-tool, call-tool, apply-openclaw
|
||||
```
|
||||
|
||||
**Login-Flow (nur das COROS-Konto des Nutzers kann das):**
|
||||
```bash
|
||||
coros-mcp login-start # gibt einen Browser-Login-Link aus (z.B. https://mcpeu.coros.com/cli/login/...)
|
||||
# Nutzer öffnet Link im Browser + meldet sich mit COROS-Konto an
|
||||
coros-mcp login-finish # "login ok" — bestätigt Session
|
||||
coros-mcp list-tools # zeigt verfügbare Tools (JSON)
|
||||
```
|
||||
Token wird in `~/.coros-mcp-skill-gateway-ts/<region>/token.json` gecacht (root-only machen: `chmod 600/700`).
|
||||
|
||||
## 🔥 Korrekte CLI-Syntax (Nicht raten!)
|
||||
|
||||
`call-tool`/`describe-tool` erwarten Flags, NICHT nur den Namen als Positional:
|
||||
```bash
|
||||
# FALSCH (gibt "unknown argument"):
|
||||
coros-mcp call-tool queryDailyHealthData
|
||||
# RICHTIG (--tool + --arguments-json):
|
||||
coros-mcp call-tool --tool queryDailyHealthData --arguments-json '{}'
|
||||
coros-mcp describe-tool --tool queryDailyHealthData
|
||||
```
|
||||
Das `--help` auf Unterbefehlen zeigt nur die Haupt-Hilfe; die Flags stehen im installierten
|
||||
Paket (`$(npm root -g)/coros-mcp/dist/*.js`, `call-tool requires --tool`, `--arguments-json`).
|
||||
|
||||
## Wertvollste Tools für Gesundheits-Detektiv
|
||||
|
||||
- `queryDailyHealthData` — 7-Tage-Health (Steps, Stress, Calories, Sleep-Übersicht je Tag)
|
||||
- `querySleepData` / `querySleepHrv` — Schlaf + HRV (P0-Merkmale)
|
||||
- `queryStressLevel` / `queryStressTimeSeries` — Stress
|
||||
- `queryAvgHeartRate` / `queryRestingHeartRate` — Puls
|
||||
- `querySportRecords` / `getActivityDetail` / `downloadActivityFitFiles` — Trainingsdaten
|
||||
- `queryRecoveryStatus` / `queryFitnessAssessmentOverview` — Erholung % + VO2max/Threshold-Pace
|
||||
|
||||
## Text-Block-Parsing (wichtige Heuristik)
|
||||
|
||||
COROS-`call-tool`-Antworten sind **Text**, mit einem 7-Tage-Block je Datum:
|
||||
```
|
||||
--- 20260815 ---
|
||||
Steps: 981 | Calories: 47 kcal
|
||||
Stress: Avg 22
|
||||
Sleep Summary: Total: 7h 46min | Deep: ...
|
||||
```
|
||||
**Pitfall:** Ein simpler "nimm erste Zahl nach Label" greift das **Datum** statt Steps (→ steps=20260815).
|
||||
**Robust:** `re.split(r'--- (\d{8}) ---', text)` → letzten Tagesblock nehmen, dort Werte ziehen.
|
||||
Umlaute-fest: `get_num(body, "Stress")` mit `re.search(re.escape(label)+r'[^\d]*?([\d.,]+)')`
|
||||
umgeht Zwischenwörter wie "Avg" (also `Stress: Avg 22` → 22).
|
||||
Datum: `'20260815'` → `'2026-08-15'` formatieren.
|
||||
|
||||
## In PocketBase schreiben
|
||||
|
||||
Direkter SQLite-Write (root) umgeht das leere `createRule`-Problem — siehe SKILL.md
|
||||
"Write-Schutz: leerer createRule blockt User-Writes". Beim INSERT **`PRAGMA table_info`** prüfen,
|
||||
welche Spalten existieren, und nur vorhandene senden (kein `datum`/`source` annehmen).
|
||||
|
||||
Cron: `0 3 * * * cd /opt/persoent_api/coros && python3 coros-persoent-poller.py >> /var/log/coros-poller.log 2>&1`
|
||||
@@ -0,0 +1,55 @@
|
||||
# Flask Deploy Pattern (PersoEnt, 19.07.2026)
|
||||
|
||||
## venv anlegen (Ubuntu 24.04)
|
||||
```bash
|
||||
apt-get install -y python3.12-venv
|
||||
python3 -m venv /var/www/<projekt>/backend/venv
|
||||
/var/www/<projekt>/backend/venv/bin/pip install flask flask-cors requests
|
||||
```
|
||||
|
||||
## systemd Service
|
||||
`/etc/systemd/system/<projekt>-api.service`:
|
||||
```ini
|
||||
[Unit]
|
||||
Description=<Projekt> API (Flask)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
User=www-data
|
||||
Group=www-data
|
||||
WorkingDirectory=/var/www/<projekt>/backend
|
||||
Environment=PATH=/var/www/<projekt>/backend/venv/bin
|
||||
ExecStart=/var/www/<projekt>/backend/venv/bin/python app.py
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
## Kritisch: Berechtigungen nach jedem File-Edit!
|
||||
`patch`/`write_file`/`sed` ändern den Owner → `chown www-data:www-data app.py` VOR `systemctl restart`!
|
||||
Sonst: `Permission denied` + Restart-Loop.
|
||||
|
||||
## Nginx (in sites-enabled/<domain>)
|
||||
```nginx
|
||||
location /api/ {
|
||||
proxy_pass http://127.0.0.1:5000/api/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
```
|
||||
`nginx -t && systemctl reload nginx`
|
||||
|
||||
## Token in Code schreiben (NICHT via sed!)
|
||||
⚠️ `sed -i "s|^PB_TOKEN = .*|PB_TOKEN = \"$TOKEN\"|"` schneidet langen Token ab (Terminal kürzt bei Display, aber das Problem: sed ersetzt mit gekürztem Wert wenn Variable im Heredoc/SSH-Kontext kaputt ist).
|
||||
**Sicher:** Python-Script nutzen:
|
||||
```python
|
||||
import re
|
||||
token = "VOLLER_TOKEN"
|
||||
with open('app.py') as f: c = f.read()
|
||||
c = re.sub(r'PB_TOKEN = ".*"', f'PB_TOKEN = "{token}"', c)
|
||||
with open('app.py','w') as f: f.write(c)
|
||||
```
|
||||
@@ -0,0 +1,32 @@
|
||||
# PocketBase 0.25 Quirks — Session Transcript (19.07.2026)
|
||||
|
||||
## Fehler 1: Collection ohne Felder
|
||||
POST `/api/collections` mit `schema:[...]` → 200, aber Collection hat NUR `id`-Feld.
|
||||
Filter/Abfragen schlagen fehl (400).
|
||||
**Fix:** PATCH mit `fields:[{name,type,id,...}]` — jedes Feld braucht `id`.
|
||||
|
||||
## Fehler 2: Filter 400
|
||||
`filter=user_id='x'` → 400
|
||||
`filter=user_id = "x"` → 200 ✅ (Leerzeichen + Double-Quotes)
|
||||
|
||||
## Fehler 3: sort=-created → 400
|
||||
`?sort=-created` am Ende der Query → 400.
|
||||
`created` ist System-Feld in 0.25, Sortierung darüber nicht erlaubt ohne Index.
|
||||
**Fix:** `sort` komplett weglassen.
|
||||
|
||||
## Fehler 4: viewRule="" reicht nicht für Filter
|
||||
GET ohne Auth-Header → 400 bei `filter=`.
|
||||
**Fix:** Admin-Token im Backend-Code (`headers={"Authorization": PB_TOKEN}`).
|
||||
|
||||
## Verifikation (End-to-End)
|
||||
```
|
||||
POST /api/health/record {"weight":73.2,"blood_pressure":"118/76","pulse":64,"user_id":"e2e_test"}
|
||||
→ {"id":"...","message":"Health record saved successfully"}
|
||||
|
||||
GET /api/health/records?user_id=e2e_test
|
||||
→ {"records":[{...}],"total":1}
|
||||
```
|
||||
|
||||
## Token-Hardcoding Gotcha
|
||||
`sed` hat Token auf `eyJhbG...4TQA` (23 Zeichen statt 224) gekürzt → 400 blieb.
|
||||
Python `re.sub` mit vollem Token string gelöst.
|
||||
Reference in New Issue
Block a user