Grotti 3.6.0: Lauf-Identität nach Filament-Pause, robustere Queue, Projekte/Aufträge list-first und API-Polish.
This commit is contained in:
1 parent
d1eb87ba25
commit
2b1e36634a
53 files changed
+4254
-636
No files matched your search
@@ -16,7 +16,7 @@ Es gibt **kein freies Spulen-Dropdown** beim Start: Grotti nimmt die Rolle(n), d
|
||||
|
||||
Während Druck oder Pause die neue Spule unter Material dem Drucker zuweisen und speichern. Grotti schließt den Abschnitt der alten Rolle und öffnet einen neuen — im Druckbericht erscheinen dann zwei Spulenzeilen und Ereignisse. Nur Filament am Gerät laden ohne Zuweisung: Abzug und Protokoll bleiben bei der alten Rolle.
|
||||
|
||||
Wenn Moonraker den Druck nach Filament-Pause als neuen History-Job führt, übernimmt Grotti die neu zugewiesene Rolle für den fortgesetzten Teil (Send- und Lauf-Spule werden beim Einlegen aktualisiert).
|
||||
Wenn Moonraker den Druck nach Filament-Pause als neuen History-Job führt, übernimmt Grotti die neu zugewiesene Rolle für den fortgesetzten Teil (Send- und Lauf-Spule werden beim Einlegen aktualisiert). Die beiden Job-IDs bleiben **ein** Protokoll-Lauf, wenn die Druckdauer fortgesetzt wird oder die Wand „druckt weiter“ gesehen hat.
|
||||
|
||||
Etiketten mit QR: [Material und mehr](Anleitung-Material.md).
|
||||
|
||||
@@ -30,7 +30,7 @@ Unter **Dateien → Optionen → ACE-Zuordnung**:
|
||||
- Ohne passende Map schreibt der Drucker oft **„cannot parse the file“** und startet nicht
|
||||
- Grotti legt die Sidecar `*.gcode.acm` an und schickt sie mit
|
||||
|
||||
Bei **Nächster passender** (kein Drucker gewählt) baut Grotti die Map erst beim Claim aus den Spulen am gewählten Drucker. Passt die Sorte nicht → Wartegrund in der Queue.
|
||||
Bei **Nächster passender** (kein Drucker gewählt) baut Grotti die Map erst beim Claim aus den Spulen am gewählten Drucker. Passt der Typ nicht (PETG statt PLA) → Wartegrund in der Queue. Meta/Matte/Silk halten nicht an; die genannte Sorte wird nur bevorzugt.
|
||||
|
||||
Externe Spule / keine ACE-Slots: keine `.acm` nötig.
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ Projekte mit eigenem Ordner: [Projekte](Anleitung-Projekte.md).
|
||||
2. **Drucker** wählen — Kacheln zeigen Live-Zustand und eingelegte Rolle. Passt die Maschine nicht, ist die Kachel blass.
|
||||
3. **Start:**
|
||||
- **Dieser Drucker** — frei und Platte klar → sofort starten; sonst Warteschlange für diesen Drucker
|
||||
- **Nächster passender** — Grotti wählt den nächsten freien Drucker mit passender Sorte (z. B. Sunlu PLA Meta, nicht Matte)
|
||||
- **Nächster passender** — Grotti wählt den nächsten freien Drucker mit passendem Typ (PLA, PETG, …). Die genannte Sorte (Meta, Matte) wird bevorzugt, wenn sie liegt; sonst reicht derselbe Typ.
|
||||
|
||||
Auswahl merkt sich der Browser (`localStorage`).
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Einrichtung
|
||||
|
||||
Stand **15.10.2026**. Grotti auf dem Haus-PC installieren und den Browser öffnen. Die Drucker brauchen vorher Moonraker (Rinkhals auf Kobra 3 und S1).
|
||||
Stand **24.10.2026**. Grotti auf dem Haus-PC oder Raspberry Pi installieren und den Browser öffnen. Die Drucker brauchen vorher Moonraker (Rinkhals auf Kobra 3 und S1).
|
||||
|
||||
## Was du brauchst
|
||||
|
||||
@@ -56,8 +56,10 @@ Dann <http://127.0.0.1:8770/> öffnen. Beim ersten Start legst du den Admin-Benu
|
||||
|
||||
- Druckstart über die API nur mit `confirm: true` (Dateien-UI: ein Klick).
|
||||
- Keine freie G-code-Konsole; Firmware-Neustart über Grotti ist gesperrt.
|
||||
- Benutzer und API-Tokens unter Einstellungen. Für Zugriff aus dem Internet: Reverse Proxy mit TLS, nicht den Entwickler-Server öffentlich freigeben.
|
||||
- Benutzer und API-Tokens unter Einstellungen. App-Kopplung: Token erzeugen und QR scannen, oder `POST /api/v1/auth/token` mit Benutzer/Passwort. Push aufs Handy: **ntfy** (kein FCM). API-Referenz: intern `docs/internal/API.md`.
|
||||
- Für Zugriff aus dem Internet: Reverse Proxy mit TLS, nicht den Entwickler-Server öffentlich freigeben.
|
||||
- `config.json` und `data/` bleiben lokal, nicht im Git.
|
||||
- Raspberry Pi: [Installation auf dem Pi](Anleitung-Installation-Pi.md) (`.deb`, Umzug Windows→Pi).
|
||||
|
||||
## Weiter
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Telegram: Bot-Token und Chat-ID, pro Ereignisart an/aus, Test-Knopf. **ntfy:** S
|
||||
|
||||
## Benutzer und API-Tokens
|
||||
|
||||
Unter **Benutzer** (nur Admin) legst du Konten mit Rolle Admin oder Operator an. Unter **API-Tokens** erzeugst du Schlüssel für Home-Automation, App oder MCP — der Klartext erscheint nur einmal (`Authorization: Bearer grt_…`). Ein Shared-`token` in der Config-Datei gilt nicht mehr.
|
||||
Unter **Benutzer** (nur Admin) legst du Konten mit Rolle Admin oder Operator an. Unter **API-Tokens** erzeugst du Schlüssel für Home-Automation, App oder MCP — der Klartext erscheint nur einmal (`Authorization: Bearer grt_…`), dazu ein QR mit Server-URL und Token zum Scannen. Alternativ stellt die App `POST /api/v1/auth/token` mit Benutzer/Passwort aus. Ein Shared-`token` in der Config-Datei gilt nicht mehr.
|
||||
|
||||
MCP (optional): Token erzeugen, dann `python -m grotti.mcp_server` mit `GROTTI_URL` und `GROTTI_TOKEN`. Für nur Lesen: `GROTTI_MCP_READ_ONLY=1`.
|
||||
|
||||
|
||||
@@ -85,8 +85,36 @@ sudo apt purge grotti # zusätzlich /etc/grotti
|
||||
# sudo rm -rf /var/lib/grotti
|
||||
```
|
||||
|
||||
## Umzug Windows → Pi
|
||||
|
||||
1. **Backup auf dem alten Rechner** (Einstellungen → Backup) oder `pg_dump` der PostgreSQL-Datenbank.
|
||||
2. Auf dem Pi Grotti per `.deb` installieren (oben).
|
||||
3. Backup einspielen (Einstellungen → Backup wiederherstellen) **oder** `pg_restore` in die DB `grotti`.
|
||||
4. Slice-Ordner nach `/var/lib/grotti/slice` kopieren (rsync/scp).
|
||||
5. Absolute Windows-Pfade in der DB umschreiben (Dry-Run zuerst):
|
||||
|
||||
```bash
|
||||
sudo -u grotti /opt/grotti/venv/bin/python /opt/grotti/scripts/pfade_umziehen.py \
|
||||
--database-url 'postgresql:///grotti?host=/var/run/postgresql' \
|
||||
--from 'D:/dein/slice' --to /var/lib/grotti/slice
|
||||
# prüfen, dann mit --apply
|
||||
```
|
||||
|
||||
(Alternativ aus dem Repo-Clone mit derselben DB-URL.)
|
||||
|
||||
6. Unter Einstellungen: `watch_dir` = `/var/lib/grotti/slice`, ggf. `fqdn` auf die Pi-URL.
|
||||
7. In Orca „Senden an Grotti“ auf `http://<pi-ip>:8770/` umstellen.
|
||||
8. `sudo systemctl restart grotti` und unter **Dateien → Ordner neu lesen**.
|
||||
|
||||
## Last auf dem Pi
|
||||
|
||||
- Watcher und Toolpath-Cache liegen unter `/var/lib/grotti/`. Bei wenig Speicher Toolpath-Cache-Ordner (`data/toolpath`) gelegentlich leeren.
|
||||
- Wenige SSE-Clients sind unkritisch; viele offene Browser-Tabs mit Kamera-Stream belasten CPU/Netz.
|
||||
- Empfohlen: Pi 4/5 mit SSD oder schneller SD, nicht parallel schwere Builds auf demselben Gerät.
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Standard lauscht auf `0.0.0.0:8770` (LAN). Für Internet: Reverse Proxy mit TLS.
|
||||
- Drucker unter **Einstellungen** anlegen (kein vorgefülltes `drucker.json` nötig).
|
||||
- Öffentliches `apt install grotti` ohne Datei: kommt mit dem Repo-Server (Phase 2).
|
||||
- `.deb` auf dem Ziel-Pi bauen (`bash packaging/build-deb.sh`), damit arm64-Wheels (`psycopg`, `cryptography`) passen.
|
||||
@@ -26,9 +26,9 @@ Unter **Aufträge** Kunden, Positionen und Fortschritt. Dateien lassen sich eine
|
||||
|
||||
## Protokoll und Statistik
|
||||
|
||||
Abgeschlossene Läufe: Dauer, Ist-Gramm, Kosten, Urteil. Zeile öffnet den **Druckbericht** (Soll/Ist, Spulen, Temperaturverlauf, Kosten-Aufschlüsselung Filament + Strom mit Tortendiagramm, Charge). **Nochmal** startet denselben Job inkl. gespeicherter ACE-Map und Bettnetz-Option.
|
||||
Abgeschlossene Läufe: Dauer, Ist-Gramm, Kosten, Urteil. Zeile öffnet den **Druckbericht** (Soll/Ist, Spulen, Temperaturverlauf, Kosten-Aufschlüsselung Filament + Strom mit Tortendiagramm, Charge). **Nochmal** startet denselben Job inkl. gespeicherter ACE-Map und Bettnetz-Option (nicht während der Druck noch läuft).
|
||||
|
||||
Nach Filament-Ende: neue Spule unter **Material** dem Drucker/ACE-Platz zuweisen (Ort setzen und speichern — das legt den Rollenwechsel im Lauf an), am Gerät laden, an der Wand **Weiter**. Nur physisch nachlegen reicht nicht. Die Zuweisung gilt auch, wenn Moonraker den Druck nach der Pause als neuen Job zählt.
|
||||
Nach Filament-Ende: neue Spule unter **Material** dem Drucker/ACE-Platz zuweisen (Ort setzen und speichern — das legt den Rollenwechsel im Lauf an), am Gerät laden, an der Wand **Weiter**. Nur physisch nachlegen reicht nicht. Die Zuweisung gilt auch, wenn Moonraker den Druck nach der Pause als neuen Job zählt — Grotti hält das als **einen** Lauf (gleiche Charge). Im Protokoll siehst du bei Rollenwechsel die Spulennamen; falls doch zwei Zeilen stehen, **Läufe holen** oder **Verbinden**.
|
||||
|
||||
Unter **Statistik** KPIs, Balkendiagramme (Druckzeit je Drucker, Verbrauch je Material), Tabellen und CSV-Export. Zeitraum per Knopf oder Von/Bis.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Projekte
|
||||
|
||||
Stand **18.10.2026**. Arbeitsmappen unter `/projekte` — getrennt von Kunden-[Aufträgen](Anleitung-Auftraege.md).
|
||||
Stand **24.10.2026**. Arbeitsmappen unter `/projekte` — getrennt von Kunden-[Aufträgen](Anleitung-Auftraege.md).
|
||||
|
||||
## Wozu
|
||||
|
||||
@@ -16,11 +16,19 @@ Ein **Projekt** bündelt Konstruktion und Druckdateien in einem Ordner unter dem
|
||||
|
||||
**Neues Projekt** — Name, Status, optional Ordner (Standard: `Projekte/<Kurzname>`). Mit gesetztem Slice-Ordner legt Grotti den Ordner an.
|
||||
|
||||
G-code und 3MF in diesem Ordner (oder dorthin [verschoben](Anleitung-Dateien.md)) werden dem Projekt zugeordnet und erscheinen in der Druck-Bibliothek.
|
||||
Filter **Aktiv** / **Fertig** / **Alle** in der Liste. G-code und 3MF in diesem Ordner (oder dorthin [verschoben](Anleitung-Dateien.md)) werden dem Projekt zugeordnet und erscheinen in der Druck-Bibliothek.
|
||||
|
||||
## Weitere Dateien
|
||||
## Detail
|
||||
|
||||
Im Projekt-Detail: Upload von STEP, STL, PDF, SolidWorks usw. — liegen im selben Ordner, erscheinen **nicht** in der Druck-Bibliothek. Download und Löschen pro Datei.
|
||||
Im Projekt siehst du Kennzahlen (Läufe, Druckzeit, Filament, Kosten) und drei Tabs:
|
||||
|
||||
- **Druckdateien** — Link zur Bibliothek (`?file=` / `?project=`), **Einreihen** pro Datei oder **Alle einreihen** (Warteschlange, mit Bestätigung)
|
||||
- **Läufe** — letzte Drucke mit Link zum Protokoll / Bericht
|
||||
- **Weitere Dateien** — STEP, STL, PDF, SolidWorks usw. (nicht in der Druck-Bibliothek)
|
||||
|
||||
Wenn jede Datei mindestens einmal ok gedruckt wurde, schlägt Grotti vor, den Status auf **fertig** zu setzen.
|
||||
|
||||
Deep-Link: `/projekte#<id>`.
|
||||
|
||||
## Löschen
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Druckerwand
|
||||
|
||||
Stand **18.10.2026**. Startseite `/` — eine Karte je Drucker. Klick öffnet das Cockpit.
|
||||
Stand **24.10.2026**. Startseite `/` — eine Karte je Drucker. Klick öffnet das Cockpit.
|
||||
|
||||

|
||||
|
||||
@@ -19,7 +19,7 @@ Aktionen auf der Karte brauchen oft **zwei Klicks** (erster Klick „scharf“,
|
||||
|
||||
| Aktion | Wann |
|
||||
| --- | --- |
|
||||
| **Pause** / **Weiter** | Während des Drucks; nach Materialwechsel Spule in Grotti an den Drucker hängen, dann **Weiter** |
|
||||
| **Pause** / **Weiter** | Während des Drucks; nach Materialwechsel gelber Hinweis mit ACE-Platz → Link zu Material (Drucker/Platz vorausgefüllt), Speichern, dann **Weiter**. Dieselbe Charge läuft weiter. |
|
||||
| **Abbrechen** | Druck oder Pause |
|
||||
| **Fehler löschen** | Zustand Fehler — räumt den hängenden Fehler, Heizung aus |
|
||||
| **Platte geräumt** | Nach Fertig, wenn die Platte wieder frei ist |
|
||||
@@ -39,7 +39,8 @@ Inhalt:
|
||||
- Status, Temps inkl. Bauraum wo verfügbar, Lüfter
|
||||
- **Steuerung:** Zieltemperaturen, Speed, Flow, Fan — fest vorgegebene Befehle, keine freie Konsole. Mit ACE zusätzlich Trockner (Temp, Dauer, Start/Stopp) und pro bestücktem Platz **an den Drucker** (Sorte und Farbe aus der Grotti-Spule)
|
||||
- **E-Stop** nur bei printing/paused/heating (Zwei-Klick). Danach oft Strom aus/an
|
||||
- **Verlauf** Temperatur und ED des aktuellen oder letzten Drucks; Link zum Druckbericht (dort Kosten: Filament + Grundlast/Düse/Bett)
|
||||
- **Verlauf** Temperatur und ED des aktuellen oder letzten Drucks; Links zu Datei und Druckbericht (dort Kosten: Filament + Grundlast/Düse/Bett)
|
||||
- **Als Nächstes** (nur wenn der Drucker frei und die Platte quittiert ist): **Nochmal** der letzten Datei, Hinweis zur Warteschlange, passende Dateien nach Familie/Material — Start mit Standardwerten oder **Anpassen…** unter Dateien
|
||||
- **3D-Pfad** der laufenden oder gewählten Datei; Objekte ausschließen (mit Bestätigung)
|
||||
- **Bettnetz**-Heatmap, wenn vorhanden
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# Grotti HTTP-API (`/api/v1`)
|
||||
|
||||
Stand für Android/App-Clients und Integrationen. Nutzer-Anleitungen bleiben unter `docs/Anleitung-*.md`.
|
||||
|
||||
## Auth
|
||||
|
||||
| Weg | Header / Cookie | CSRF |
|
||||
| --- | --- | --- |
|
||||
| Session | Cookie nach `POST /auth/login` | `X-CSRF-Token` bei mutierenden API-Calls |
|
||||
| Bearer | `Authorization: Bearer grt_…` | nicht nötig |
|
||||
|
||||
**Token ausstellen**
|
||||
|
||||
- Eingeloggt: `POST /tokens` `{name, role?}` → Klartext einmalig in `token`
|
||||
- Ohne Session (App): `POST /auth/token` `{username, password, device_name?, role?}` → `201` mit `token` + `user` (Rate-Limit wie Login)
|
||||
|
||||
Öffentlich ohne Login: `/auth/status`, `/auth/login`, `/auth/token`, `/auth/setup`, `/version`.
|
||||
|
||||
Rollen: `operator` ≤ `admin`. Admin-only u. a. Benutzer, Backup, volle Einstellungen, Drucker anlegen.
|
||||
|
||||
**Medien mit Query-Token** (nur GET): `?token=grt_…` für Kamera, Live-Vorschau, Datei-Vorschau (`/dateien/<id>/vorschau`), Projekt-Assets.
|
||||
|
||||
## Fehler
|
||||
|
||||
```json
|
||||
{"error": "Text", "code": "kurzcode"}
|
||||
```
|
||||
|
||||
HTTP-Status: 400/401/403/404/409/429/503. Bild-Endpunkte liefern bei fehlendem Bild ebenfalls dieses JSON (nicht nur leeren 404).
|
||||
|
||||
## Pagination
|
||||
|
||||
Listen akzeptieren `limit` / `offset` wo sinnvoll. Antwort bleibt ein **JSON-Array**; Gesamtzahl im Header `X-Total-Count`.
|
||||
|
||||
Ohne `limit` liefern `/dateien` und `/spulen` die volle Liste (Web-UI).
|
||||
|
||||
## Bestätigung
|
||||
|
||||
Mutierende Druckaktionen brauchen oft `"confirm": true`:
|
||||
|
||||
- `POST /senden`, `POST /druck-queue`, `POST /ablauf`
|
||||
- `POST /projekte/<id>/einreihen`, `POST /auftraege/<id>/einreihen`
|
||||
- `POST /drucker/<host>/abraeumen`
|
||||
- `steuern` mit `emergency_stop`, `clear_error`, `exclude_object`
|
||||
|
||||
## Server-Info
|
||||
|
||||
`GET /version` → `{name, version, api: 1, features: [...]}`.
|
||||
|
||||
## SSE Live
|
||||
|
||||
`GET /stream` (Session-Cookie oder Bearer). Events:
|
||||
|
||||
| Event | Bedeutung |
|
||||
| --- | --- |
|
||||
| `ping` | Keepalive (~15 s), Payload `{}` |
|
||||
| `drucker` | Live-Zustand der Drucker geändert |
|
||||
| `queue` | Druckwarteschlange geändert |
|
||||
| `senden` | Sende-Auftrag geändert |
|
||||
| `ereignis` | Neues Ereignis / Aktivität |
|
||||
| `aufgaben` | Offene Aufgaben (Platte …) |
|
||||
|
||||
Ohne Stream: Client pollt `/drucker?live=1`, `/jetzt`, `/druck-queue`.
|
||||
|
||||
## Push an Geräte
|
||||
|
||||
Grotti sendet **kein** FCM. Empfohlen: **ntfy** (Topic unter Einstellungen). Die App kann denselben Topic abonnieren; Secrets (ntfy-Token) bleiben serverseitig.
|
||||
|
||||
## Wichtige Ressourcen
|
||||
|
||||
| Bereich | Pfade |
|
||||
| --- | --- |
|
||||
| Drucker | `GET /drucker?live=1`, `POST /drucker/<host>/pause\|fortsetzen\|abbrechen` (Alias `resume`/`cancel`), `…/steuern`, `…/abraeumen` |
|
||||
| Dateien | `GET /dateien?family=&material=&project_id=&host=&q=&limit=&offset=`, Upload multipart, `…/vorschau`, `…/pfad` |
|
||||
| Senden / Queue | `POST /senden`, `GET /senden-zuletzt?host=&file_id=`, `GET\|POST /druck-queue?host=&status=`, `POST /ablauf` |
|
||||
| Projekte | `GET\|POST /projekte`, `GET /projekte/<id>`, `…/laeufe`, `POST …/einreihen`, Assets |
|
||||
| Aufträge | `GET\|POST /auftraege`, Positionen, `…/einreihen` |
|
||||
| Material | `GET /spulen?aktiv=1`, Akte, Einlegen |
|
||||
| Protokoll | `GET /laeufe`, `GET /laeufe/<id>`, `GET /statistik?project_id=` |
|
||||
| Kamera | `GET /kamera?host=`, `…/stream` |
|
||||
|
||||
Pfadnamen sind überwiegend **deutsch**. Aktionen: deutsch + englische Alias für Pause/Resume/Cancel.
|
||||
|
||||
## MCP
|
||||
|
||||
`python -m grotti.mcp_server` nutzt dieselbe REST-API (Teilmenge). Siehe `docs/internal/MCP.md`.
|
||||
@@ -134,6 +134,16 @@ Ohne Map: Rinkhals füllt oft Identity T0→Platz0 … — deshalb Grotti bei ei
|
||||
| Slots anzeigen | Moonraker `mmu` (Rinkhals) | `mmu_ace.py` |
|
||||
| Kein Firmware-Restart | abgelehnt | Rinkhals #32, `patch_klipper_restart` |
|
||||
|
||||
## History nach Filament-Pause
|
||||
|
||||
Runout, Rolle tauschen, Fortsetzen: Anycubic schließt den History-Job als `cancelled` und öffnet eine neue Job-ID. Beobachtet am 2026-10-25 auf Kobra 3 A1 (`000040` → `000041`):
|
||||
|
||||
- Der neue `start_time` liegt etwa 0,04 s nach dem `end_time` des alten.
|
||||
- `print_duration` und `total_duration` sind an beiden Einträgen gleich und laufen weiter. Der Zähler beginnt nicht bei 0.
|
||||
- `/server/history/list` liefert neueste zuerst. Grotti sortiert pro Drucker nach `start_time`, sonst kommt die Fortsetzung vor ihrem Vorgänger dran (`jobs.history_in_order`).
|
||||
- Nach dem Zusammenlegen hängt die alte Job-ID in `run_jobs` am Lauf. `store.history_job_retired` überspringt sie, sonst schreibt sie `cancelled` zurück.
|
||||
- Zusammenlegen nur mit Beweis: `print_duration` fortgesetzt und Lücke ≤ 5 s, oder Watcher `paused→printing` (`docs/internal/Lauf-Identitaet.md`).
|
||||
|
||||
## Offene Grenzen
|
||||
|
||||
- Quellcode von **gkapi** (Task-State-Machine, exakte 10101-Bedingungen) ist nicht öffentlich.
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Lauf-Identität: ein physischer Druck = ein Lauf
|
||||
|
||||
## Regel
|
||||
|
||||
Zusammengelegt wird nur mit **Beweis**, nicht nur wegen Zeit und Dateiname:
|
||||
|
||||
1. Der neue Moonraker-Job hat `print_duration` ≥ Dauer des Vorgängers − 30 s, und die Lücke `end→start` ist höchstens 5 s (Anycubic setzt `end_time` erst beim Fortsetzen).
|
||||
2. Oder der Watcher hat `paused → printing` bei gleicher Datei gesehen (`watcher_resume`, `printers.active_run_id`).
|
||||
|
||||
Ein Neustart mit `print_duration` ≤ 60 s wird nie zusammengelegt.
|
||||
|
||||
## Pfade
|
||||
|
||||
| Pfad | Datei |
|
||||
| --- | --- |
|
||||
| Beweis | `jobs.is_filament_resume` |
|
||||
| Fold | `jobs.fold_filament_resume` |
|
||||
| Abgleich | `jobs.sync_history` (Advisory-Lock) |
|
||||
| Job-Aliase | `run_jobs` + `history_job_retired` |
|
||||
| Leere Hülle | übersprungen in `_sync_one_job` |
|
||||
| Live-Abzug | erst History; Fallback nach 10 min (`LIVE_DEDUCT_FALLBACK_S`) |
|
||||
| Manuell | `POST /api/v1/laeufe/<id>/verbinden` |
|
||||
|
||||
## Queue
|
||||
|
||||
In `print_active` heißt nur `complete` fertig. `cancelled`/`standby` werden entprellt (`TERMINAL_CONFIRM_HITS`). Echter Abbruch → Status `cancelled`, nicht „Fertig.“.
|
||||
|
||||
## Reparatur
|
||||
|
||||
`scripts/laeufe_reparieren.py` (Probelauf standardmäßig, `--apply` nach Freigabe).
|
||||
Verwaiste `live:`-Läufe nur an offenen History-Jobs **derselben Datei**; `adopt_live_run` greift geschlossene Live-Läufe nur ≤ 2 h.
|
||||
@@ -27,7 +27,7 @@ Jedes Ereignis landet in `events` und auf der Seite `/aktivitaet`. Telegram ist
|
||||
| `fertig` | Watcher, `printing`→`complete` | nein, Platte unter „Aufgaben“ | an |
|
||||
| `fehler` | Watcher, Zustand `error` | ja | an |
|
||||
| `abbruch` | Watcher, `printing`/`paused`→`cancelled`/`standby` | ja | wie `fehler` |
|
||||
| `offline` | Watcher, ≥2 Polls weg; auch „wieder erreichbar“ | ja | an |
|
||||
| `offline` | Watcher, ≥2 Polls weg; auch „wieder erreichbar“. Beendet den Lauf nicht und zieht nichts ab — der letzte Druckzustand bleibt gemerkt | ja | an |
|
||||
| `spule` | Watcher, Rest unter Job-Gewicht | ja | an |
|
||||
| `queue` | Dispatcher: Queue-Fehler, Ablauf gestoppt | ja | an |
|
||||
| `start` | Watcher: Druck startet, Pause, Fortsetzen | nein | aus |
|
||||
|
||||
@@ -6,5 +6,8 @@ Nutzer-Handbuch: `docs/Anleitung-*.md`.
|
||||
|
||||
| Datei | Inhalt |
|
||||
| --- | --- |
|
||||
| [API.md](API.md) | HTTP-API für App/Integrationen (Auth, confirm, SSE, Filter) |
|
||||
| [openapi.yaml](openapi.yaml) | OpenAPI-Skizze (Client-Generator) |
|
||||
| [MCP.md](MCP.md) | MCP-Server und Tool-Ablauf |
|
||||
| [Makros-Kobra.md](Makros-Kobra.md) | G-code/Makros Kobra S1 und 3: Nicht-tun, Vanilla-Alltag, ACE, LeviQ/Wipe |
|
||||
| [Anycubic-Stack.md](Anycubic-Stack.md) | GoKlipper, MQTT-Start/Stop, ACE-Kanäle |
|
||||
@@ -0,0 +1,180 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: Grotti API
|
||||
version: "1"
|
||||
description: |
|
||||
JSON unter /api/v1. Auth: Session-Cookie oder Bearer grt_….
|
||||
Mutierende Session-Calls brauchen Header X-CSRF-Token.
|
||||
Siehe docs/internal/API.md.
|
||||
servers:
|
||||
- url: /api/v1
|
||||
paths:
|
||||
/version:
|
||||
get:
|
||||
summary: Server-Info
|
||||
security: []
|
||||
responses:
|
||||
"200":
|
||||
description: name, version, api, features
|
||||
/auth/login:
|
||||
post:
|
||||
summary: Session-Login
|
||||
security: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [username, password]
|
||||
properties:
|
||||
username: { type: string }
|
||||
password: { type: string }
|
||||
responses:
|
||||
"200":
|
||||
description: user + csrf
|
||||
/auth/token:
|
||||
post:
|
||||
summary: Passwort → API-Token (App)
|
||||
security: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [username, password]
|
||||
properties:
|
||||
username: { type: string }
|
||||
password: { type: string }
|
||||
device_name: { type: string }
|
||||
role: { type: string, enum: [operator, admin] }
|
||||
responses:
|
||||
"201":
|
||||
description: token (einmalig) + user
|
||||
/drucker:
|
||||
get:
|
||||
summary: Druckerliste
|
||||
parameters:
|
||||
- name: live
|
||||
in: query
|
||||
schema: { type: integer, enum: [0, 1] }
|
||||
responses:
|
||||
"200":
|
||||
description: Array Drucker
|
||||
/drucker/{host}/{action}:
|
||||
post:
|
||||
summary: Pause / Fortsetzen / Abbrechen
|
||||
parameters:
|
||||
- name: host
|
||||
in: path
|
||||
required: true
|
||||
schema: { type: string }
|
||||
- name: action
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
enum: [pause, fortsetzen, abbrechen, resume, cancel]
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
/dateien:
|
||||
get:
|
||||
summary: Bibliothek
|
||||
parameters:
|
||||
- { name: family, in: query, schema: { type: string } }
|
||||
- { name: material, in: query, schema: { type: string } }
|
||||
- { name: project_id, in: query, schema: { type: integer } }
|
||||
- { name: host, in: query, schema: { type: string } }
|
||||
- { name: q, in: query, schema: { type: string } }
|
||||
- { name: limit, in: query, schema: { type: integer } }
|
||||
- { name: offset, in: query, schema: { type: integer } }
|
||||
responses:
|
||||
"200":
|
||||
description: Array Dateien; Header X-Total-Count
|
||||
headers:
|
||||
X-Total-Count:
|
||||
schema: { type: integer }
|
||||
/senden:
|
||||
post:
|
||||
summary: Druck starten oder einreihen
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [file_id, confirm]
|
||||
properties:
|
||||
file_id: { type: integer }
|
||||
hosts: { type: array, items: { type: string } }
|
||||
confirm: { type: boolean }
|
||||
when_free: { type: boolean }
|
||||
ace_maps: { type: object }
|
||||
bed_leveling: { type: boolean }
|
||||
responses:
|
||||
"202":
|
||||
description: jobs
|
||||
/druck-queue:
|
||||
get:
|
||||
summary: Warteschlange
|
||||
parameters:
|
||||
- { name: host, in: query, schema: { type: string } }
|
||||
- { name: status, in: query, schema: { type: string } }
|
||||
- { name: limit, in: query, schema: { type: integer } }
|
||||
- { name: offset, in: query, schema: { type: integer } }
|
||||
responses:
|
||||
"200":
|
||||
description: Array; X-Total-Count
|
||||
post:
|
||||
summary: Einreihen
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [file_id, confirm]
|
||||
responses:
|
||||
"200":
|
||||
description: Queue-Eintrag
|
||||
/projekte/{id}/einreihen:
|
||||
post:
|
||||
summary: Projektdateien einreihen
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
required: true
|
||||
schema: { type: integer }
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [confirm]
|
||||
properties:
|
||||
confirm: { type: boolean }
|
||||
file_ids: { type: array, items: { type: integer } }
|
||||
menge: { type: integer }
|
||||
responses:
|
||||
"201":
|
||||
description: jobs
|
||||
/stream:
|
||||
get:
|
||||
summary: SSE Live-Events
|
||||
responses:
|
||||
"200":
|
||||
description: text/event-stream (drucker, queue, senden, ereignis, ping)
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: grt_
|
||||
cookieAuth:
|
||||
type: apiKey
|
||||
in: cookie
|
||||
name: session
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- cookieAuth: []
|
||||
Reference in new issue
Block a user