Grotti 2.2.0: Auth, API-Tokens, MCP und Doku-Trennung.

This commit is contained in:
Dominic Gundel committed 2026-10-15 10:53:12 +02:00
1 parent b20f8a7779
commit 5a3716b26a
65 files changed
+2212 -198

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
# ACE-Farben in Grotti (Moonraker)
Stand **12.10.2026**: An Kobra 3 **da26** (`10.10.1.60`) liefert Moonraker die ACE-Spulenfarben unter **`mmu`**, nicht unter `filament_hub` (steht oft nicht in `objects/list`).
- Relevante Felder: `gate_color` (Hex `AARRGGBB` oder `RRGGBB`), `gate_material`, optional `gate_status` bzw. Listen pro Platz.
- Grotti liest das in `grotti/moonraker.py` (`extract_slots`) und zeigt die Farben als Swatches auf der Druckerwand.
- Fallback: `filament_hub` / `filament_hubs[].slots` mit `color` und `type`, falls vorhanden.
## Senden: welches Material welcher Slot
12.10.2026: In `Dateien` bei Druckern mit Slots die Slice-Farbe (`paint_index`) einem ACE-Platz (`ams_index`, 0-basiert) zuordnen. Grotti schreibt `Dateiname.gcode.acm` und lädt sie vor `print/start` hoch. Ohne Sidecar: auf `da26` oft HTTP 400 „cannot parse the file“. Siehe [Grotti-Senden-Queue](Grotti-Senden-Queue).
## 12.10. Abbruch `tulip pla.gcode` auf da26 — kein ACE-Blink
Beobachtung: Material ging initial zum Extruder, ACE-Slot blinkte nicht, kein Nachschub vom ACE. Andere Kobra 3 (92ee/6e41) drucken dieselbe Datei von der externen Spule weiter; nicht angefasst.
Ursache (Moonraker-Log `da26`, lesend):
- Job `000009` (tot-printing, 0 mm Filament) und `00000A` (~695 s Druck, ~658 mm) beide `cancelled` auf `10.10.1.60`.
- Bei jedem MQTT-Start: `mmu_ace.patch_print_data` → `detected_tools unknown … skipping auto-feed (no trailer found)`. Ohne Trailer/Tools kein Auto-Feed → kein Slot-Blinken, kein ACE-Nachschub.
- Metadaten-Extrakt für die Datei scheitert (`Unable to extract metadata` / `mmu_ace_metadata` abgebrochen).
- Grotti-Sidecar `tulip pla.gcode.acm`: `paint_index 0 → ams_index 1` (Farbe Datei `#000000`, Slot-Farbe `002B49`). Parallel liegt `tulip pla.acm` mit Identity-Map T0–T3 → Plätze 0–3. MQTT-Payload nutzte die 4er-Map, nicht Grotti-1er-Map.
- Start ohne `bed_leveling`: Grotti übergibt `ams_settings` nur als `.acm`; Moonraker baut die MQTT-`ams_settings` selbst. `auto_leveling: 0`.
- Initialer Extruder-Feed ohne Blink: Restfilament im Pfad / Extrusion aus dem Schlauch, nicht ACE-Auswahl. Nach Abbruch: `mmu.gate`/`tool` = −1, `active_filament` leer; vier Plätze weiter belegt.
Grotti-DB `sends`: da26 zweimal `tulip pla.gcode` (ohne persistiertes `ace_map`; Mapping nur in der Queue im Speicher und in der `.acm` auf dem Drucker). Queue-API leer nach Abschluss.
Nächster Start auf da26: gewünschten ACE-Platz in Grotti setzen (bei schwarzer Slice-Farbe zuletzt Platz 1 = dunkelblau). Alte `tulip pla.acm` nicht mit Grotti-`.gcode.acm` vermischen. Ohne erfolgreichen Trailer/Metadaten bleibt Auto-Feed aus — dann blinkt der ACE weiterhin nicht.
## Fix Grotti (12.10.2026, nach Diagnose)
Anycubic erwartet die Sidecar als **gleichen Basename + `.acm`**: Start `tulip pla.gcode` → `tulip pla.gcode.acm`. Grotti lädt Grotti-Map zusätzlich nach `tulip pla.acm` hoch (Überschreiben), damit eine alte Identity-Map nicht gewinnt.
Mit gesetzter ACE-Zuordnung startet Grotti den Druck per **MQTT** und setzt `ams_box_mapping` aus Grotti-Map — nicht mehr nur über Moonraker-`print/start` (dort Identity-Fallback / falsche `.acm`). Bettnetz-Checkbox steuert weiter nur `auto_leveling`.
13.10.2026: Fehlt `; referenced_tools` beim Senden mit ACE-Zuordnung, hängt Grotti ihn an — aus T-Befehlen, sonst aus `paint_info` / der gewählten `paint_index`-Map (Einzelfarbe oft `0`). Zusätzlich die Rinkhals-Kopfzeile `; processed by MmuAcePatcher`. Warnung nur noch, wenn danach immer noch kein Trailer da ist.
## 13.10.2026 — MQTT 10101 trotz Moonraker `cancelled` (da26 / Gridfinity)
Nach Grotti-Send `Gridfinity_bin_1x1x6u_PLA_0.2_49m32s.gcode` an `da26` (`10.10.1.60`): Moonraker `print_stats.state` = `cancelled`, Anycubic-MQTT-Task blieb aktiv → nachfolgender Start mit **code 10101** („已存在打印任务“ / Print task already exists). Grotti `abbrechen` traf nur Moonraker `/printer/print/cancel`, nicht den Anycubic-Task.
Klärung: MQTT `action: stop`, `taskid: "-1"` auf Topic `…/slicer/printer/{modelId}/{deviceId}/print` (Rinkhals-Docs). Report `state: stopping`, code 200; danach MQTT-Info `data.state: free`. Grotti `abbrechen` macht seither Moonraker-cancel **und** best-effort MQTT-stop (`anycubic_lan.cancel_print`). Wenn MQTT-stop nicht greift: Display abbrechen oder Reboot als letzte Option.
+81
View File
@@ -0,0 +1,81 @@
# Grotti: Abräumen und weiterdrucken (Kobra 3)
Stand **13.10.2026**.
Nach dem Druck kann Grotti auf der **Kobra 3** Bett und Düse abkühlen und die Teile mit dem Extruderkopf nach vorn vom Bett schieben. Danach startet der nächste Schritt oder die nächste Wiederholung. Die **Kobra S1** ist geschlossen — Abräumen dort ist noch offen.
## Quittierung vs. Schieben
- **Wand „Platte geräumt“:** Nur Quittierung. Tabelle `bed_status`, API `POST /api/v1/drucker/<host>/abraeumen` mit `confirm`. Fährt keine Bahn. `needs_clear` setzt der Watcher bei Druckende/Abbruch/Fehler (nicht schon beim Start). Solange gesetzt, startet „wenn frei“ den nächsten Job nicht.
- **Ablauf (Dateien):** Drucken → Abkühlen → Schieben → Tasten, dann der nächste Druck mit LeviQ. Eine Bestätigung beim Einreihen; Schieben läuft ohne zweite Rückfrage. Nach erfolgreichem Schieben ist die Platte automatisch quittiert.
- **Normale Drucke** und „Nochmal“ schieben nicht — nach dem Druck muss man auf der Wand quittieren.
## Oberfläche
Unter **Dateien** erscheint die Karte **Ablauf**, sobald mindestens eine Kobra 3 gewählt ist. Der Nutzer baut die Schritte selbst:
- **Drucken** — Datei, Bettnetz (LeviQ) standardmäßig an
- **Abkühlen** — Heizung aus, Lüfter an, Düse in die Bettmitte, warten bis Bett/Düse unter den Grenzen
- **Schieben** — Bahn mit einstellbaren Zahlen, danach Lüfter aus
- **Tasten** — kein `G28 Z`. **Mit aktivem LeviQ-Netz** (kein `BED_MESH_CLEAR`): `Result` = Höhe über der lokalen Platte. Bezugspunkt + Tastpunkt (x/y) einstellbar. Maß: letztes `Result is z=` aus dem Store. Über 1 mm = Restteil.
Darunter **wie oft hintereinander**. Einreihen legt einen Eintrag in die Warteschlange „Wenn frei“. **Schieben testen** fährt die Bahn auf einer freien Kobra 3 (Bestätigung „Bett ist leer“). Werte merkt sich der Browser lokal.
Vor dem Bett braucht es eine Kiste. Der Weg nach vorn muss frei sein.
Standardreihenfolge: `Drucken → Abkühlen → Schieben → Tasten` (dann Wiederholung, LeviQ am Druckstart). Altes 3×3-Prüfen (`probe_count` / `mesh_*`) wird übersprungen und in der Schrittleiste nicht mehr gezeigt.
## Standardbahn (editierbar)
- Abkühlen: Heizung aus, Idle verlängern (Stepper an), Park Bettmitte. Z zuerst relativ **+40 mm** (gegen Z-max gekappt), dann X125/Y125. Hub wird geprüft.
- Schieben: hoch → Y hinten (`y_from` Default **250**) → Z noch klar? → **langsam** auf `z_push` (`F400`, nie F3000). Z prüfen; bei Abweichung Abbruch. Nur die X-Spuren aus der Config. `M107`.
- Tasten: Mesh an. Absolut auf Freiraum, ein `PROBE` am Tastpunkt, `Result is z=`. Kein `SET Z=100` / Bezug-Null (hat die Koordinate nur verfälscht). Z zu tief nach Leerlauf → Abbruch, Idle soll Stepper an lassen.
Y klein ist vorn. Das Wischpad liegt hinten (~Y 260) und bleibt außerhalb der Bahn. Grotti klemmt Eingaben (X 0–250, Y 0–235, Z-Schieben 0,6–30, Z-Umsetzen bis 80, Tempo 5–80 mm/s).
## Technik
- Kein Makro auf dem Drucker (`printer.cfg` schreibt Rinkhals neu). Grotti schickt G-code per Moonraker `/printer/gcode/script`.
- Modul `grotti/eject.py`, Steuerung in `print_queue.py`.
- Ablauf = eine Queue-Zeile mit `steps` (JSON), `step_index`, `repeat_total`, `repeat_done`, `phase`.
- Nach dem Druck kein `G28` (Z-Home würde in die Teile fahren). Slice-Ende Present nur `X250 Y220` — **kein Z-Hub**. Abkühlen: Heizung aus, Lüfter an, relativ Z+40 (≤ Z-max), dann X 125 Y 125. Schieben: anheben, Spur hinten ansetzen, langsam senken, nach vorn, hinten parken, `M107`.
- Abräumen braucht gehomed Achsen. Slice-Ende der Kobra 3 enthält `M84; disable motors` (nach Present) — danach sofort unhomed. Bei Ablauf mit Cool/Sweep entfernt Grotti `M84`/`M18` beim Upload. Zusätzlich: `SET_IDLE_TIMEOUT` schon während des Drucks und alle 120 s beim Abkühlen; wenn trotzdem unhomed, `SET_KINEMATIC_POSITION` mit der **echten** letzten Toolhead-XYZ (Present), nie erfundene Z. Kein automatisches `G28` bei belegtem Bett. „Schieben testen“ (Bett leer bestätigt) homt mit `G28`.
Gestoppter Ablauf: Seitenleiste ▶ setzt beim gespeicherten `step_index` fort (`PATCH /druck-queue/<id>` mit `status: running, resume: true`).
- Tasten: Mesh aus, Rand-PROBE + Z-Null (Retract 2 mm), Mitte-PROBE, letztes `Result is z=` gegen `max_delta_mm`. Nie `G28 Z`. Nie Toolhead-Z nach dem Retract. Fortsetzen bei gemeldetem Z unter Bett erlaubt (nur relativ hoch).
- Druckfehler oder Abbruch stoppt den Ablauf — kein Weiterschieben.
## Trockenlauf (Crash-Fix prüfen)
Bett **leer**, ein Drucker reicht. Ziel: Cool-Hub und Anfahren hinten testen, **ohne** `G0 Z{z_push}`.
`PROBE` fährt nur bis Trigger — nicht wie `G0 Z2` zwangsweise auf Koordinate. Trotzdem **nicht** mit Z8 starten (nach dem Crash zu unheimlich).
**Lauf A — sicher (zuerst):**
1. `G28`, Düse wischen.
2. `G90` → `G0 Z40 F1800` → Present `X250 Y220`.
3. Cool-Park: relativ Z+40 (≤ Z-max), Mitte. Erwartung: Z ≈ 80, Mitte.
4. `sweep_go_back_high` (hoch + Y hinten). Erwartung: hinten, Z klar oben.
5. Statt Senken: **`PROBE`**. Erwartung: `Result is z=` typisch &lt; 1 mm; sofort `G0 Z40`.
6. Stop. Keine Y-Bahn, kein `z_push`.
**Lauf B — niedrige „letzte Schicht“ (nur wenn A sauber):**
Wie A, aber Schritt 2 mit `G0 Z15` (nicht Z8). Cool +40 → Z ≈ 55. Wieder nur PROBE, nie `G0 Z2`.
Optional ganz am Ende: einmal `sweep_lower` mit `F400` + `assert_clear`, Bett leer — nur nach A+B ok.
Grotti-Schalter „Trockenlauf / PROBE statt z_push“ noch nicht gebaut — bis dahin manuell oder `_tmp_`-Skript mit `eject.*_script`.
## Grenzen
- Nur Familie `kobra_3`.
- Teile höher als die Umsetzhöhe trifft die Düse auf dem Weg nach hinten.
- Festsitzendes Material (z. B. PETG) schiebt die Düse nicht zuverlässig ab.
## Backlog (später)
Ideen liegen in Grotti unter **Ideen** (Status „später“), nicht nur hier:
- **Abdeckungen gegen Verkanten** — Führung/Rutsche vorn, damit geschobene Teile nicht an Bettkante, Rahmen oder in der Kiste verkanten und den nächsten Lauf blockieren.
- **Kobra S1** — eigener Abräum-Ablauf (geschlossen, Tür).
Erledigt: Antasten / Bettnetz vergleichen (Schritt **Prüfen**, 13.10.2026).
+141
View File
@@ -0,0 +1,141 @@
# Anycubic-Stack: wie Druck, Abbruch und ACE wirklich laufen
Stand **15.10.2026**. Quellen: Anycubic [Klipper-go](https://github.com/ANYCUBIC-3D/Klipper-go), [K3-klipper-mcu](https://github.com/ANYCUBIC-3D/K3-klipper-mcu); Rinkhals `kobra.py`, `mmu_ace.py`, `mmu_ace_metadata.py`, Docs `firmware/mqtt.md` und `firmware/goklipper.md` (lokal unter `D:\Projekte\_ref\`). Nicht geraten — am Quellcode und an der Rinkhals-Doku gelesen.
## Schichten
```mermaid
flowchart TB
subgraph host [PC]
Grotti[Grotti]
end
subgraph printer [Kobra 3 / S1]
Moonraker[Moonraker + kobra.py]
MmuAce[mmu_ace + mmu_ace_metadata]
Gkapi[gkapi MQTT Broker]
Gklib[gklib GoKlipper]
Ui[K3SysUi Touch]
Mcu[MCU Firmware]
AceHw[ACE Hardware]
end
Grotti -->|"HTTP :80 upload / print/start / cancel"| Moonraker
Grotti -->|"MQTTS :9883 print start/stop"| Gkapi
Grotti -->|"HTTP :18910 info/ctrl Credentials"| Gkapi
Moonraker -->|"bei print/start oft MQTT lokal :2883"| Gkapi
Moonraker --> Gklib
MmuAce --> Moonraker
Gkapi --> Gklib
Ui --> Gkapi
Gklib --> Mcu
Gklib --> AceHw
```
| Komponente | Offen? | Rolle |
| --- | --- | --- |
| **gklib** (GoKlipper) | teilweise ([Klipper-go](https://github.com/ANYCUBIC-3D/Klipper-go)) | Klipper-Host: Motion, Heater, `virtual_sdcard`, ACE-G-codes |
| **MCU** | teilweise ([K3-klipper-mcu](https://github.com/ANYCUBIC-3D/K3-klipper-mcu)) | Stepper/Sensoren |
| **gkapi** | nein (proprietär) | MQTT-Broker, Druck-Tasks, Cloud/LAN, Credentials `:18910` |
| **K3SysUi** | nein | Touch-UI |
| **Rinkhals Moonraker** | ja | Vanilla Moonraker + `kobra.py` + `mmu_ace*` |
Anycubic schreibt in den READMEs: proprietäre Features sind gekapselt. MQTT-Taskverwaltung und Cloud stecken nicht in Klipper-go.
## Zwei Welten beim Druckstart
1. **Klipper-Pfad** (Moonraker `POST /printer/print/start` → gklib `virtual_sdcard`): setzt `print_stats`, lädt G-code in gklib.
2. **Anycubic-Task** (MQTT `type: print`, `action: start` an Topic `…/slicer/printer/{modelId}/{deviceId}/print`): legt den Job an, den Display und Cloud kennen; steuert LeviQ/`auto_leveling`, ACE-Map in der Payload.
Rinkhals `kobra.py` `patch_mqtt_print` hängt den Moonraker-`print/start` oft auf MQTT um (`mqtt_print_file`), lokal `127.0.0.1:2883`. Grotti kann denselben Start von außen über **MQTTS `:9883`** mit Credentials aus `:18910` machen — dann mit eigener `task_settings` und `ams_settings` (siehe [Senden-Queue](Senden-Queue)).
### MQTT Start (belegt)
Aus Rinkhals-Doku `firmware/mqtt.md` und `kobra.py`:
- Publish: `anycubic/anycubicCloud/v1/slicer/printer/{modelId}/{deviceId}/print`
- Report: `…/printer/public/{modelId}/{deviceId}/print/report`
- Pflichtfelder Start: `filename`, `filetype` (1), `taskid`
- Optional: `ams_settings.ams_box_mapping`, `task_settings.auto_leveling` (0/1 Integer)
- Rinkhals und Grotti setzen `taskid` auf eine Zufallszahl; die Doc-Minimalbeispiel nutzt `"-1"` — beides kommt vor
### MQTT Stop (belegt) — warum 10101
```json
{ "type": "print", "action": "stop", "data": { "taskid": "-1" } }
```
Moonraker `/printer/print/cancel` trifft nur gklib (`print_stats` → `cancelled`). Der Anycubic-Task in gkapi bleibt → nächster MQTT-Start mit **code 10101** („Print task already exists“ / chinesische Meldung). Rinkhals `mqtt_print_file` kennt 10101 und **retryt einmal**; Grotti sendet bei Abbruch Moonraker-cancel **und** MQTT-stop.
### Task-Hygiene (wie Rinkhals)
Vorbild: `kobra.py` `mqtt_print_file` + `firmware/mqtt.md`. **Kobra 3 und Kobra S1** — dieselbe gkapi-API (`:18910` / MQTTS `:9883`). Gemessen 15.10.2026: S1 `modelId` 20025 / `state: free`, K3 `modelId` 20024; `project: null` zählt nicht als busy. Abräumen-Ablauf bleibt nur Kobra 3.
| Schritt | Verhalten in Grotti |
| --- | --- |
| Vor MQTT-Start | `ensure_task_free`: `info/query`; wenn `state` nicht free oder `project` gesetzt → MQTT-stop, bis zu 3× pollen |
| Vor Moonraker-`print/start` | dieselbe Hygiene best-effort (Rinkhals wandelt Start oft intern zu MQTT um) |
| gkapi kurz weg | `print_info` → `None`, negativer Cache ~45 s; Start über Moonraker läuft weiter |
| Report **10101** | einmal cancel + 1,5 s Pause + erneuter Start |
| Report **10107** | klare Filament-Meldung |
| Live | Snapshot-Feld `anycubic_task` (`state`, `task_id`, `filename`) aus gecachtem `print_info` (TTL ~10 s); Wand zeigt die Zeile |
Codes und Retry: `grotti/anycubic_lan.py` (`message_for_code`, `ensure_task_free`, `start_print`).
### Report-Timeout
`kobra.py` wartet max. 30 s auf `print/report`. Nach Bettnetz/`auto_leveling` kann der Report später kommen, der Druck trotzdem laufen (`info/query` → `project.task_id`). Grotti prüft bei Timeout deshalb `print_info` (MQTT `info/query`).
## FIRMWARE_RESTART
In **Klipper-go** ist der Befehl implementiert wie bei Klipper:
- `Cmd_FIRMWARE_RESTART` → `Request_restart("firmware_restart")` (`gcode.go`)
- Nach dem Reactor-Ende: Event `project:firmware_restart` → MCU `_firmware_restart` (`k3c.go`, `Mcu.go`)
Auf dem Gerät **deadlockt gklib** trotzdem (beobachtet, Rinkhals #32): Log zeigt Annahme des Befehls, danach keine weitere Verarbeitung, Moonraker bleibt in `startup`. Rinkhals `patch_klipper_restart` **lehnt** `RESTART` / `FIRMWARE_RESTART` unter GoKlipper ab. Grotti ebenso. Config: Display-Reboot oder Strom. Hängenden Fehler: **Fehler löschen** (`SDCARD_RESET_FILE`), nicht Firmware-Restart.
Der Open-Source-Code erklärt den Intent des Befehls; die Deadlock-Ursache steckt vermutlich im ausgelieferten Binary / der Anbindung an gkapi — nicht im veröffentlichten Happy Path.
## ACE
### In gklib (Klipper-go `extras_ace.go`)
G-codes: `ACE_CHANGE_TOOL`, `ACE_FEED`, `ACE_RETRACT`, `ACE_SET_SLOT`, Trocknen, Endless Spool, … Kommunikation über Seriell (`extras_ace_commun.go`). `Get_status` liefert ACE-`info` + Endless-Spool-Flags — **nicht** das Moonraker-Objekt `mmu` mit `gate_color`.
### In Rinkhals Moonraker (`mmu_ace.py`)
- Emuliert Happy-Hare-ähnliches **`mmu`**-Objekt (`gate_color`, `gate_material`, …) für Clients
- Beim MQTT-Start: `register_print_data_patcher` → wenn keine `ams_settings` in der Payload, baut Identity-/TTG-Map aus den Gates und hängt `ams_settings` an
- Liest/schreibt Sidecar **`basename.acm`** neben dem G-code; ohne ACE kann sie nach `.acm.disabled` umbenannt werden
- Auto-Feed-Logik vor Start, wenn nichts im Toolhead ist (Issue #464)
### Upload-Metadaten (`mmu_ace_metadata.py`)
Beim Moonraker-Upload: ganze Datei lesen, ggf. umschreiben. Fingerprint `; processed by MmuAcePatcher`. Trailer `; referenced_tools = 0,2,…`. Ohne Trailer: gklib „skipping auto-feed (no trailer found)“. Grotti setzt Fingerprint + Trailer vor dem Upload, damit der teure Durchlauf entfällt ([Senden-Queue](Senden-Queue)).
### `.acm` vs MQTT-`ams_settings`
Zwei Kanäle für dieselbe Zuordnung:
| Kanal | Wann |
| --- | --- |
| Datei `name.gcode.acm` / `name.acm` auf dem Drucker | Stock/gklib liest Sidecar beim Start |
| MQTT-Feld `data.ams_settings` | gkapi / Rinkhals-Patcher legt Map in die Payload |
Ohne Map: Rinkhals füllt oft Identity T0→Platz0 … — deshalb Grotti bei eigener ACE-Zuordnung per MQTT mit expliziter Map startet und beide `.acm`-Varianten hochlädt ([ACE-Live](ACE-Live)).
## Was Grotti bewusst nutzt
| Aktion | Weg | Beleg |
| --- | --- | --- |
| Einfacher Start | Upload + Moonraker `print/start` → Rinkhals MQTT | `kobra.py` `patch_mqtt_print` |
| Start mit Bettnetz oder ACE-Map | Upload (+ `.acm`) + Grotti-MQTT `:9883` | `anycubic_lan.start_print`, mqtt.md |
| Abbruch | Moonraker cancel + MQTT stop `taskid:-1` | `mqtt_stop_print`, mqtt.md |
| Credentials | `:18910/info` + `/ctrl` AES | `anycubic_lan.credentials` |
| Slots anzeigen | Moonraker `mmu` (Rinkhals) | `mmu_ace.py` |
| Kein Firmware-Restart | abgelehnt | Rinkhals #32, `patch_klipper_restart` |
## Offene Grenzen
- Quellcode von **gkapi** (Task-State-Machine, exakte 10101-Bedingungen) ist nicht öffentlich.
- Deadlock in **FIRMWARE_RESTART** ist beobachtet und in Rinkhals abgefangen, Root Cause im Binary nicht freigelegt.
- Kobra X / K4-Stack ist ein anderes System (C++-Klipper), nicht GoKlipper — für unsere Drucker irrelevant.
+25
View File
@@ -0,0 +1,25 @@
# Chargen, Etiketten und 3D-Viewer
Stand 14.10.2026.
## Spulen-Chargen
Jede Spule in Grotti bekommt automatisch eine `chargen_nr` der Form `S-YYYYMMDD-0001`. Derselbe Wert steht in `code` und auf dem Etikett. Optional: `hersteller_charge` vom Rollenetikett.
Scannen: auf Material den Code tippen oder per USB-Handscanner (Tastatur + Enter). Lookup `GET /api/v1/spulen/by-code/<code>`. QR auf dem Etikett öffnet `/material?spool=<id>`.
Etikett: `/etikett/<id>`, Druck über den Browser. QR per lokalem `qrcode.min.js` (`web/static/`). Basis-URL für den QR kommt aus Einstellungen → **FQDN** (`config.json` Key `fqdn`); leer = Host der aktuellen Anfrage. Beispiel: `https://grotti.example.de/material?spool=12`.
## 3D-Pfad
`GET /api/v1/dateien/<id>/pfad` liefert einen gedünnten Toolpath (bis ca. 120 000 Punkte, plus `layer_breaks`, Cache-`version` 5) aus dem lokalen G-code. Die Dateien-Seite und das Cockpit zeichnen ihn mit Three.js.
Darstellung: nur Extrusionswege. Preview = orange. Live: gedruckt leicht verdickt grün, offen grau; Schnitt nach `progress`. Parser akzeptiert `E.39`/`Z.6`. Bett: Gitter 10 mm, mm-Sprites, Legende.
**Exclude-Object** (nur Klipper/Moonraker): Status aus `exclude_object`. Polygone im Viewer; Liste unter dem Pfad; Zwei-Klick → `POST .../steuern` Action `exclude_object` mit `confirm`. Fehlt Modul/DEFINEs → Panel aus. BambuLab später eigener Adapter.
Cockpit-Live: Kamera-Frames werden unsichtbar vorgeladen (letztes Bild bleibt), `no-cam`-Layout nur ohne Kamera-URL. 3D-Orbit bleibt beim 5‑s-Poll erhalten (`setPath` nur beim ersten Laden / Dateiwechsel).
Auf der Wand: Caption `Dateiname · % · Schicht a/b`, sonst „Datei nicht in Grotti“.
Bedienung: Orbit / Zoom / Schwenken / Doppelklick-Reset; Tippen auf Polygon wählt das Objekt.
+20
View File
@@ -0,0 +1,20 @@
# Grotti-Dateien Metadaten
`stored_path` liegt nur in der DB und in Rohzeilen (`get_file_row`, `list_file_rows`). Die API-Antwort (`file_out` / `GET /api/v1/dateien`) enthält den Pfad bewusst nicht.
`POST /api/v1/dateien/neu-lesen` liest Watch-Ordner neu und ruft `refresh_file_meta` auf Rohzeilen auf — nicht auf `list_files`, sonst `KeyError: 'stored_path'`.
## Was der Parser liest
Kopfkommentare **und** Footer (letzte ~128 KB). Anycubic/Orca legen Gewicht, Zeit und Prozesswerte oft ans Dateiende.
In der Bibliothek gespeichert:
| Feld | Bedeutung |
|------|-----------|
| `filaments` | Typ/Farbe (`paint_info` oder `filament_type`/`filament_colour`) |
| `weight_g` / `estimated_s` | Filamentgewicht und Druckzeit |
| `printer_family` | aus `printer_model` |
| `slice_meta` | jsonb: `layer_height`, `layers`, `max_z_mm`, `nozzle_mm`, `temp_nozzle`, `temp_bed`, `slicer` |
Anzeige: Material, Zeit, Gewicht, Schichthöhe, Maschine, Projekt immer in der Zeile; Rest im Info-Popup bzw. unter der gewählten Datei. Nach Parser-Update: **Ordner neu lesen**.
+55
View File
@@ -0,0 +1,55 @@
# Grotti: Alltag und Oberfläche
Stand **14.10.2026**, v1.4.0.
## Design
- Dunkles UI mit Orange-Akzent, Lucide-ähnliche Stroke-Icons (SVG-Sprite in `layout.html`).
- Kurze Seitenuntertitel; Technik-/Config-Hinweise liegen in der Doku, nicht als Fließtext in den Formularen.
- Einheitliche Seitenköpfe, Toolbar-Filter, kompakte Tabellen, Formularfelder mit kleinen Labels, Empty-States mit Icon.
- **Anzeige pro Browser** (Einstellungen → Dieser Browser): `Desktop` oder `Regal (Touch)` in `localStorage grotti-display`. Shortcut `?shelf=1` / `?shelf=0`. Regal: größere Controls, kompakte Nav (Drucker/Kameras/Warteschlange), Sidebar default eingeklappt.
- Seitenmenü einklappbar (Icon-Rail, `grotti-nav-collapsed`). Unter 760 px weiter Bottom-Nav.
## Seitenleiste
- Hauptmenü: Drucker, Dateien, Kameras, Aktivität, Warteschlange, Protokoll, Material, Projekte. Darunter Einstellungen, Ideen, Doku (mit Icons); Fuß **© by dominic@gundel.dev** und Version (Changelog).
- Live-Leiste, eigene Scrollfläche, Boxen auf- und zuklappbar (Zustand in `localStorage` `grotti-live-closed`):
- **Läuft jetzt**: laufende Aufträge und Abläufe mit Schrittleiste, Durchlauf `n/m`, aktueller Phase und ×-Abbrechen; auch Direktstarts ohne Queue.
- **Wartet**: wartende Aufträge mit Ziel, Priorität hoch/runter, Abbrechen; die letzten Fehler mit Fortsetzen/ausblenden.
- **Aufgaben**: Platte belegt ohne Ablauf — „geräumt“ per Zwei-Klick.
- **Senden**: laufende Uploads mit Fortschritt.
- **Alerts**: ungelesene wichtige Ereignisse, „gelesen“, Link auf Aktivität.
- Aktualisierung: Senden 2 s, Queue 4 s, Aufgaben/Alerts 8 s.
## Wand
- Karte je Drucker: Zustandschip, Fertig-Uhrzeit groß, Fortschrittsbalken, drei Kacheln (Rest, Schicht, Läuft), Temperaturbalken Düse/Bett mit Ziel, Datei. Klick auf die Karte öffnet das Cockpit. Pause, Abbrechen, Fehler löschen und Platte geräumt bleiben auf der Karte (Zwei-Klick).
- Laufender Ablauf als Streifen mit Schrittleiste und Abbrechen. Wartende Aufträge für diesen Drucker als Hinweis.
- Streifen „Fehler“ mit **Fehler löschen** und „Platte belegt“ mit **Platte geräumt** (während eines Ablaufs ausgeblendet, der räumt selbst).
- **Cockpit** (Hash `/#<host>`): Kamera (Frame-Vorladen; zugeklappt oder ohne Kamera → eine Spalte, kein leeres 16:10), Status, Temps inkl. Bauraum wenn vorhanden, Lüfter-Icons, Steuerung, 3D-Pfad (Exclude-Object), Bettnetz. E-Stop nur bei printing/paused/heating, Zwei-Klick. Live vom Watcher.
## Dateien und Senden
- Links Bibliothek: Suche, Filter Maschine/Projekt, Vorschaubild; Zeile zeigt Material, Zeit, Gewicht, Schichthöhe, Maschine, Projekt; Details per Info-Popup. Metadaten aus G-code-Kopf **und** Footer. Hochladen / Ordner neu lesen.
- Rechts der Flow: Datei → Drucker (Live-Zustand, eingelegte Rolle) → Wann (Jetzt / Wenn frei / Nächster freier) oder Reiter Ablauf.
- Kein freies Spulen-Dropdown: Abzug nimmt die Rolle(n) am Ziel-Drucker zur Startzeit. ACE: Zuordnung Datei-Farbe → Platz. Warnung bei Material-Mismatch oder zu wenig Rest.
- Startknopf mit Zwei-Klick bei „Jetzt“. Auswahl in `localStorage` `grotti-send-v1`.
## Material
- Reiter **Spulen** / **Sorten**. Scan-Feld im Seitenkopf.
- Spulen: Filter Alle / Am Drucker / Regal / Wenig Rest + Suche. Formular nur bei Auswahl oder „Neue Spule“ (× schließt). Ort = Drucker · ACE/extern bzw. Regal.
- Sorten: Profile (Dichte, mm³/s, Preis, Maschinen). Anlegen/ändern/löschen; „Spule damit anlegen“ wechselt zum Spulen-Reiter.
## Warteschlange
- Seite `/warteschlange`: Filter Läuft / Wartet / Fehler / Historie. Priorität, Abbrechen, Fortsetzen.
- Unter **Läuft** / **Alle aktiv** auch Direktstarts aus `/laeuft` (ohne Queue-Zeile), mit Fortschritt und Link zur Wand.
## Aktivität
- Zeitstrahl nach Tagen. Filter in der URL. Ungelesene Alerts markiert.
## Kameras und Handy
- Kamerakacheln mit Live-Zustand (`live=1`). Snapshot-Cache 2 s. Unter 760 px: Schnellmenü Drucker / Dateien / Aktivität.
+89
View File
@@ -0,0 +1,89 @@
# Glossar: Moonraker, Mainsail und der Rest
Stand **15.10.2026**. Begriffe rund um Kobra 3 / S1, Rinkhals und Grotti. Architektur-Details: [Anycubic-Stack](Anycubic-Stack).
## Die Schichten in einem Satz
| Schicht | Rolle |
| --- | --- |
| **MCU-Firmware** | Läuft auf der Drucker-Platine: Stepper, Heizer, Sensoren. |
| **gklib (GoKlipper)** | „Gehirn“ auf dem Linux der Kobra — Anycubics Go-Port von Klipper. |
| **gkapi** | Anycubic-Dienst: MQTT, Cloud/LAN, Druck-Tasks, Touch-Anbindung. |
| **Moonraker** | HTTP/WebSocket-API vor gklib — das, was Apps und Grotti ansprechen. |
| **Mainsail / Fluidd** | Web-Oberflächen *auf* Moonraker (Live-Steuerung, Konsole). |
| **Grotti** | Eure lokale Farm-Seite: Queue, Wand, Material, Abräumen — spricht Moonraker (+ MQTT wo nötig). |
| **Rinkhals** | Community-Aufsatz auf der Stock-Firmware: bringt Moonraker, SSH, Apps. |
```mermaid
flowchart TB
UI[Mainsail / Fluidd / Grotti / Orca]
UI --> MR[Moonraker]
UI -.->|Start mit ACE/Bettnetz, Stop| MQTT[gkapi MQTT]
MR --> GK[gklib GoKlipper]
MQTT --> GK
Touch[K3SysUi Touch] --> MQTT
GK --> MCU[MCU]
GK --> ACE[ACE]
```
## A–Z
### ACE
Anycubic Color Engine — Multimaterial-Box (vier Plätze). Ansteuerung in gklib (`ACE_*`-G-codes); Zuordnung Farbe→Platz oft über Sidecar **`.acm`** und/oder MQTT **`ams_settings`**. In Moonraker erscheint das bei Rinkhals oft als Objekt **`mmu`** (`gate_color`, …).
### Fluidd
Web-UI für Klipper/Moonraker, Alternative zu Mainsail. Gleiche API, anderes Aussehen. Bei Rinkhals typisch Port **4408**.
### FIRMWARE_RESTART
Klipper-Befehl: Host + MCU neu laden. Auf **GoKlipper** deadlockt gklib oft (Rinkhals #32). Grotti und Rinkhals lehnen ihn ab → Display-Reboot oder Strom.
### gklib / GoKlipper
Anycubics Klipper-Host in Go (Open Source: [Klipper-go](https://github.com/ANYCUBIC-3D/Klipper-go)). Entspricht grob dem klassischen `klippy`. Dateiname auf dem Drucker: `gklib`.
### gkapi
Geschlossener Anycubic-Dienst neben gklib. Betreibt den lokalen MQTT-Broker, Druck-Tasks, Credentials (`:18910`), Anbindung Touch/Cloud. Hier sitzt der Task, der bei Abbruch **10101** verursacht, wenn nur Moonraker-cancel kommt.
### Grotti
Dieses Tool: lokale deutsche Seite für mehrere Moonraker-Drucker — Wand, Warteschlange „wenn frei“, Abräumen (Kobra 3), Material, Kosten, Telegram. Kein Slicer, keine Cloud.
### K3SysUi
Anycubic-Touch-Oberfläche (geschlossen). Rinkhals patcht sie, um das eigene Menü einzuhängen.
### Klipper
Open-Source-Firmware-Architektur: schwere Berechnung auf dem Host (Linux), MCU führt Bewegungen aus. Vanilla-Klipper ist Python (`klippy`); auf der Kobra 3 läuft die Go-Variante.
### Mainsail
Beliebte Web-UI für Moonraker: Status, Temp, G-code-Konsole, Dateien, Webcam. Bei Rinkhals oft Port **80** / **4409**. Fühlt sich „direkter“ an als Grotti, weil die Konsole offen ist.
### Moonraker
API-Server vor Klipper/gklib: HTTP und WebSocket. Upload, `print/start`, Pause, `gcode/script`, `objects/query`, Kamera-Proxy. **OctoPrint-Äquivalent** in der Klipper-Welt. Grotti und Mainsail sind Clients davon — Moonraker ist nicht die schöne Oberfläche selbst.
### MQTT (hier)
Protokoll zu **gkapi**, nicht zu Moonraker. Topics wie `…/slicer/printer/{modelId}/{deviceId}/print`. Genutzt für Druck **start/stop**, Info-Query, optional Temp/Speed/Licht. Ports: lokal oft **2883**, von außen TLS **9883**.
### OctoPrint
Klassische Host-Software für (meist) Marlin-Drucker: Web-UI, Plugins, oft G-code-**Streaming** Zeile für Zeile. Bei Klipper/Rinkhals ersetzt durch **Moonraker + Mainsail/Fluidd** (Datei komplett hochladen, Host spielt ab).
### Orca / AnycubicSlicerNext
Slicer auf dem PC. Schicken fertige `.gcode` per Moonraker (oder USB). Kein Teil der Drucker-Firmware.
### printer.cfg / printer.generated.cfg
Klipper-Konfiguration. Auf Anycubic oft generiert/neu geschrieben; Rinkhals warnt vor wildem Editieren (Boot-Risiko). Z-Offset u. a. in `printer_mutable.cfg`.
### Repetier-Server
Host-Software ähnlich OctoPrint (eigene UI, oft Streaming/direktes G-code). Gleicher Kontrast: auf der Kobra ist der Host **gklib**, die API **Moonraker**.
### Rinkhals
Community-Overlay auf Stock-Kobra-OS: Moonraker, Mainsail/Fluidd, SSH, App-Store, ohne die Stock-Firmware zu ersetzen. Installiert der Nutzer selbst (SWU). Docs: [rinkhals-community/Rinkhals](https://github.com/rinkhals-community/Rinkhals).
### SSH
Root-Shell auf dem Drucker-Linux (Rinkhals: Port 22, `root` / `rockchip`). Zum Debuggen, Logs, App-Properties — **nicht** der normale Weg für Drucksteuerung (das bleibt Moonraker).
### SWU
Anycubic-Update-Paket auf USB (Ordner `aGVscF9zb3Nf`). Darüber kommen Stock-Updates und Rinkhals/Installer.
### virtual_sdcard
Klipper-Modul: G-code liegt als Datei auf dem Host und wird von gklib abgespielt. Deshalb Upload + Start statt Streaming über WLAN.
### 10101
Anycubic-MQTT-Fehler: „Druck-Task existiert schon“. Typisch nach Abbruch nur über Moonraker, ohne MQTT `action: stop`.
+31
View File
@@ -0,0 +1,31 @@
# Haftung
**Entwurf.** Kein anwaltlich geprüfter Text. Vor einem Verkauf mit Preis, Abo oder Shop kommen Impressum, Datenschutz und AGB dazu. Diese Seiten nennen keinen Preis.
Stand **14.10.2026**. Was die Drucker können müssen, steht unter [Voraussetzungen](Voraussetzungen.md).
## Grundsatz
Grotti flasht nicht, schickt keine Firmware und beschreibt keine Signatur-Umgehung, keinen Root-Exploit und keine Umgehung einer Downgrade-Sperre.
Der Nutzer richtet seinen eigenen Drucker ein, bevor er Grotti nutzt. Verlinkt wird nur die Installationsseite von Rinkhals: <https://github.com/rinkhals-community/Rinkhals>. Die Warnung, die der Hersteller selbst einblendet, bleibt seine.
Anycubic schließt ungenehmigte Änderungen von der Garantie aus. Rinkhals ist ein Community-Aufsatz; die ursprüngliche Software bleibt darunter, der Aufsatz lässt sich wieder entfernen. Ob Anycubic einen Fall annimmt, entscheidet Anycubic.
Creality lässt beim offiziellen Root den Hardware-Service stehen und gibt dafür keine Software-Unterstützung. Fremde Firmware ist von der Garantie ausgenommen (<https://wiki.creality.com/en/warranty-policy>).
Bambu Lab deckt Schäden durch inoffizielle Firmware nicht ab. Der Entwicklermodus ist eine eigene Funktion von Bambu Lab, mit der Warnung, die Bambu Lab selbst einblendet.
Die gesetzliche Mängelhaftung für den Drucker bleibt beim Verkäufer des Druckers. Ein Disclaimer Grotti wischt Verletzung, Vorsatz und grobe Fahrlässigkeit nicht weg.
Unbeaufsichtigtes Drucken, Hitze und Brandgefahr stehen vor dem ersten Start, nicht im Kleingedruckten.
Es gibt kein Auftrags-Flashen und keine vorgeflashten Drucker.
## Vor dem ersten Start
Düse und Bett werden heiß. Den ersten Lauf behält der Nutzer im Blick. Der Satz dazu steht oben, nicht erst hier: Unbeaufsichtigtes Drucken, Hitze und Brandgefahr stehen vor dem ersten Start, nicht im Kleingedruckten.
## Was Grotti beim Start schon verlangt
Druckstarts brauchen eine Bestätigung (`confirm: true`). Es gibt keine freie G-code-Konsole und kein `FIRMWARE_RESTART`.
+30
View File
@@ -0,0 +1,30 @@
# MCP-Server
Stand **15.10.2026**. Agenten steuern Grotti nur über die HTTP-API mit einem API-Token.
## Start
```powershell
$env:GROTTI_URL = "http://127.0.0.1:8770"
$env:GROTTI_TOKEN = "grt_…" # unter Einstellungen → API-Tokens
$env:GROTTI_MCP_READ_ONLY = "1" # optional: nur lesen
.\.venv\Scripts\pip install mcp
.\.venv\Scripts\python.exe -m grotti.mcp_server
```
## Tools
| Tool | Schreibend | Confirm |
| --- | --- | --- |
| `list_printers`, `get_printer`, `list_running`, `list_queue`, `list_events`, `list_files`, `list_spools`, `get_version` | nein | — |
| `pause_printer`, `resume_printer` | ja | — |
| `cancel_printer`, `clear_bed`, `enqueue_*` | ja | `confirm=true` |
| `steer_temp`, `steer_speed`, `steer_fan` | ja | — |
Nicht exponiert: freie G-code, Firmware-Restart, E-Stop, User/Token/Settings.
## Sicherheit
- Token mit Rolle Operator reicht für den Alltag; Admin-Token vermeiden.
- Read-only-Flag für vorsichtige Agents.
- Server ideal nur lokal; Grotti selbst hinter Login/TLS.
+50
View File
@@ -0,0 +1,50 @@
# Grotti Ops: Queue, Alerts, Steuerung
Stand 14.10.2026.
## Einstellungen
Seite `/einstellungen`: Druckernamen/Host/Familie/gesperrt (PostgreSQL `printers`, `drucker.json` nur Seed), Automatik (`auto_queue` in `config.json`), Telegram, Energie. Live-API und Wand nutzen die DB-Namen. Gesperrte Drucker und `auto_queue: false` verhindern neue „wenn frei“-Starts; laufende Abläufe laufen weiter.
## Live-Quelle
`StateWatcher` pollt die Drucker (ca. 10 s). `GET /drucker?live=1` und der Print-Queue-Dispatcher lesen diesen Stand. Nur wenn der Cache älter als ~60 s / 30 s ist, wird einmal direkt abgefragt. Lange Ablauf-Schritte (Upload, Schieben, Tasten) laufen in Worker-Threads.
## Druckwarteschlange „wenn frei“
Dateien → „Wenn frei“ oder „Nächster freier“ → `when_free` in `POST /api/v1/senden`. Einträge in `print_queue`; Dispatcher startet, sobald der Host (oder ein freier Drucker) `standby`/`complete`/`cancelled` ist, Platte frei und nicht gesperrt. **Alle freien Drucker im selben Tick** (Upload/Start je Host im Worker parallel) — nicht einer alle ~8 s. Sidebar und Seite `/warteschlange`. Sofort-Senden bleibt über die Send-Queue (mehrere Worker). Hängender Fehler wird nur geräumt, wenn für den Host ein Auftrag wartet. Ablauf: Phase `starting` vor Upload; Timeout 10 min ohne `print_active`.
## Spulen
Ort = Regal oder Drucker (+ `ace_slot`). Unique-Index `(printer_host, ace_slot)`. Beim Start löst Grotti die Spule(n) aus Host + `ace_map` auf; Materialtyp der Datei muss passen. `sends` speichert `ace_map` und `bed_leveling`. Nach „fertig“ synct der Watcher das Protokoll und zieht Spulen ab (ACE anteilig).
## Telegram / Ereignisse
Jedes Ereignis landet in `events` und auf der Seite `/aktivitaet`. Telegram ist pro Art abschaltbar und wird je Host und Art entprellt (Watcher 60 s, Queue 30 s). Das Speichern wird nie entprellt. Gemeinsamer Weg: `notify.record()`.
| Art | Woher | Alert | Telegram-Standard |
| --- | --- | --- | --- |
| `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 |
| `spule` | Watcher, Rest unter Job-Gewicht | ja | an |
| `queue` | Dispatcher: Queue-Fehler, Ablauf gestoppt | ja | an |
| `start` | Watcher: Druck startet, Pause, Fortsetzen | nein | aus |
| `ablauf` | Dispatcher: Start, Schritte, Wiederholung, fertig; App: eingereiht, von Hand abgebrochen | nein | aus |
„Alert“ heißt: ungelesen, steht in der Sidebar. `start`, `ablauf` und `fertig` werden gleich als gelesen gespeichert (`store.QUIET_EVENT_KINDS`), damit unter Alerts nur Probleme stehen.
„Aufgaben“ (Sidebar, `GET /api/v1/aufgaben`): Drucker mit `bed_status.needs_clear`, ohne laufenden Queue-Eintrag und nicht am Drucken. Knopf „geräumt“ ruft `POST /api/v1/drucker/<host>/abraeumen` mit `confirm: true` (Zwei-Klick), fährt nichts.
API: `GET /api/v1/ereignisse?limit=&host=&kind=a,b&since=<ISO>&unread=1`, `POST /api/v1/ereignisse/gelesen`.
Bei `print_stats.state=error` ist `message` auf Anycubic oft leer. Grotti ergänzt dann nur aus dem G-code-Log (echte `Must home`-/Fehlerzeilen) oder „Druck sofort abgebrochen“. Leeres `homed_axes` allein gilt nicht als Fehler — Idle schaltet die Stepper ab, das ist normal.
Nach Fehler: Watcher schaltet Bett/Düse/Lüfter aus (nicht weiter heizen). Vor Start und zwischen Ablauf-Schritten: hängender `print_stats=error` wird per `SDCARD_RESET_FILE` geräumt und der Druck gestartet. Kein Homing-Zwang vorher — der Slice-Start macht `G28`. Nur mitten im Druck (`print_active`) bricht ein error den Ablauf ab.
## Steuerung
Wand-Details: Düse/Bett, Speed/Flow/Fan, E-Stop und Exclude-Object über `POST /api/v1/drucker/<host>/steuern` (Whitelist, keine freie Konsole; `exclude_object` braucht `confirm` + `name`). Abbruch weiterhin Moonraker-cancel + MQTT-stop.
**Kein Firmware-Restart:** Auf GoKlipper/Rinkhals deadlocked `FIRMWARE_RESTART` gklib bis Power-Cycle ([Rinkhals #32](https://github.com/jbatonnet/Rinkhals/issues/32)). Grotti lehnt den Befehl ab. `printer.cfg` nur über Display-Reboot oder Strom. Hängenden `print_stats=error`: Wand **Fehler löschen** (`SDCARD_RESET_FILE` + Heizung aus).
+5
View File
@@ -0,0 +1,5 @@
# Interne Technik-Notizen
Diese Dateien sind für Entwicklung und Agenten — **nicht** unter `/doku` in der App.
Nutzer-Handbuch: `docs/Anleitung-*.md`.
+80
View File
@@ -0,0 +1,80 @@
# Grotti: Senden und Start dauert
Stand **15.10.2026** (Stack-Quellen und Task-Hygiene in [Anycubic-Stack](Anycubic-Stack)). Spulen kommen vom Ziel-Drucker zur Startzeit (`ace_map` + eingelegte Rolle), nicht aus einem freien Dropdown. `sends` speichert `ace_map` und Bettnetz für „Nochmal“.
## Warum Sekunden / Minuten
Über Grotti-App (`Dateien` → Starten) läuft der Aufruf nicht nur lokal: die Datei wird als **ein synchroner Multipart-POST** an Moonraker hochgeladen (`/server/files/upload`), danach startet `/printer/print/start`. Der HTTP-Request endet erst, wenn Moonraker die Datei geschrieben und oft Metadaten/MMU-Preprocess angeschoben hat — bis dahin wirkt die Queue „still“.
Messung 12.10.2026, `tulip pla.gcode` (~**50,4 MB** in `data/eingang`, typisch daneben ~2 MB): Upload auf `da26` (`10.10.1.60`) **~67 s** laut Moonraker-Log (`201 POST … 67106 ms`), plus kurze `.acm`-Sidecar (~87 ms) und Start. Gesamter Queue-Job ~**96 s**. Kleine Dateien bleiben im Sekundenbereich.
13.10.2026: Die lange Pause bei „50/50 MB“ ist Rinkhals `mmu_ace_metadata.py`. Der liest die ganze Datei auf der Kobra, bevor der Upload-HTTP antwortet, und schreibt sie bei gefundenen Werkzeugen noch einmal um. Grotti setzt vorher die Kopfzeile `; processed by MmuAcePatcher`. Bei Prusa, Super, Orca, Bambu oder AnycubicSlicerNext und Werkzeugen (`T0`, `MMU_CHANGE_TOOL`) hängt sie `; referenced_tools = …` an — dieselbe Zeile, die der Drucker sonst selbst schreibt. Danach überspringt er diesen Durchlauf. Stehen die Bytes, wechselt die Anzeige auf „liest die Datei…“. Grotti neu starten (`use_reloader=False`).
Vorher prüft Grotti Online-Status und ob schon gedruckt wird — laufende Drucke werden nicht abgebrochen. Die Rinkhals-Touch-UI können wir nicht umbauen; Fortschritt betrifft Grotti unter `:8770`.
## Queue (Grotti)
Ab 12.10.2026: Start legt Hintergrund-Aufträge an (`pending` → `running` → `done`/`error`). Pro gewähltem Drucker ein Queue-Job; mehrere Worker. UI pollt ~500 ms. Beim Hochladen zeigt die Queue jetzt **MB-Fortschritt** (urllib liest den Body gestreamt); Timeout skaliert mit Dateigröße (ca. 180–600 s). Bestätigung „Druck wirklich starten“ bleibt Pflicht. Nach Python-Änderung Grotti neu starten (`use_reloader=False`).
## Slice-Ordner / Upload
`watch_dir` in `config.json` zeigt auf den Slice-Ordner der Bibliothek. Ohne Ordner geht Hochladen über die Dateien-Seite trotzdem.
## ACE / `.acm`
Drucker mit ACE/MMU-Plätzen (`mmu.gate_color` / `gate_material`, Grotti-`slots`) brauchen vor `print/start` eine Sidecar-Datei `*.gcode.acm`. In der UI unter Optionen → ACE-Zuordnung (Datei-Farbe → Platz). Ohne passende `.acm` lehnt der Drucker oft mit „cannot parse the file“ ab — Details hier, nicht als Banner in der App:
```json
{"use_ams":true,"ams_box_mapping":[{"paint_index":0,"paint_color":[r,g,b],"material_type":"PLA","ams_index":1,"ams_color":[r,g,b]}]}
```
`paint_index` = Farbe in der Slice-Datei (0-basiert), `ams_index` = ACE-Platz 0-basiert. Externe Spule / keine Slots: kein `.acm`. Keine Community-Makros (`ACE_SET_SLOT` / `G9111`).
AnycubicSlicerNext schreibt die Farben oft als `; paint_info: [{...}]` statt `filament_type` — Grotti liest das seit 12.10.2026 mit.
## Bettnetz / auto_leveling beim Start (12.10.2026)
Rinkhals `kobra.py` `mqtt_print_file` baut den Anycubic-MQTT-Start so (Topic `…/slicer/printer/{modelId}/{deviceId}/print`, lokal `127.0.0.1:2883`):
```json
{
"type": "print",
"action": "start",
"msgid": "<uuid>",
"timestamp": 1660201929871,
"data": {
"filename": "job.gcode",
"filepath": "/",
"taskid": "123456",
"task_mode": 1,
"filetype": 1,
"task_settings": {
"auto_leveling": 1,
"vibration_compensation": 0,
"flow_calibration": 0
}
}
}
```
`auto_leveling` ist **0/1** (Integer). `1` löst die Anycubic-Nivellierfolge beim Jobstart aus (LeviQ / Netz messen) — nicht über G-code `LEVIQ_*` / `BED_MESH_CALIBRATE` / `SAVE_CONFIG` von Grotti. Ohne Grotti-Flag kommt der Wert aus der **globalen** App-Property `40-moonraker` → `mqtt_print_auto_leveling` (Default `False`). Extra-Felder an Moonraker `POST /printer/print/start` werden ignoriert. App-Property setzen geht nur per SSH (`set_app_property 40-moonraker mqtt_print_auto_leveling True`), keine HTTP-API.
### Grotti-Verhalten
| Checkbox / ACE | Startweg |
| --- | --- |
| kein Bettnetz, keine ACE-Map | Upload + Moonraker `print/start` → Rinkhals MQTT (globale `auto_leveling`, oft 0) |
| ACE-Map gesetzt | Upload + `.acm` (Basename + Stamm-Variante) + **Grotti-MQTT** mit `ams_settings` aus der Map; `auto_leveling` nur 1 wenn Bettnetz an |
| Bettnetz an | wie ACE-Map-Pfad, `auto_leveling:1` |
Credentials holt Grotti ohne SSH: `GET :18910/info` → signiertes `POST :18910/ctrl` → AES-Decrypt (Username/Password/`deviceId`). Port **2883** ist vom PC zu; **9883** (TLS, Cert-Check aus) ist offen. Mit Grotti-MQTT liegt `ams_box_mapping` explizit in der Payload (kein Moonraker-Identity-Fallback). Ohne `; referenced_tools`-Trailer: Warnung in UI/Queue, kein Fake-Trailer.
**Abbruch:** Grotti `abbrechen` = Moonraker `/printer/print/cancel` **plus** MQTT `action: stop` / `taskid: "-1"` (gleicher Topic wie Start). Nur Moonraker-cancel lässt den Anycubic-Task stehen → nächster MQTT-Start mit **10101**. 13.10.2026: Gridfinity auf da26 so hängen geblieben, per MQTT-stop freigemacht (`data.state: free`).
### Task-Hygiene vor dem Start (15.10.2026)
Vor jedem Start (MQTT und reiner Moonraker-Pfad) räumt Grotti einen hängenden gkapi-Task best-effort — **Kobra 3 und S1**: `print_info` → ggf. MQTT-stop → poll. Kommt trotzdem **10101**, einmal cancel + 1,5 s + Retry (wie Rinkhals). Ist gkapi kurz unerreichbar, läuft der Moonraker-Start trotzdem (negativer Cache ~45 s). Live zeigt die Wand `anycubic_task` aus demselben `info/query` (Cache ~10 s). Details: [Anycubic-Stack](Anycubic-Stack) Abschnitt Task-Hygiene.
**Start-Report verspätet:** 13.10.2026 ~21:35, A2 (`10.10.1.59`), Ablauf 2/5 direkt nach der Bettnetz-Prüfung. Innerhalb von 30 s kam kein `print/report`, der Druck lief trotzdem an. `info/query` (Topic `…/slicer/printer/{modelId}/{deviceId}/info`, Antwort auf `…/info/report`) zeigte `project.task_id` = Grotti-`taskid`, `state: auto_leveling`. Moonraker meldet in dieser Phase noch `print_stats.state: complete` vom Vorauftrag. Grotti prüft deshalb bei einem Start-Timeout bis zu dreimal `info/query`. Steht dort die eigene `taskid`, gilt der Start als erfolgreich (`anycubic_lan.print_info`).
Fallback ohne MQTT: global in Rinkhals Touch → Manage apps → 40-moonraker → Auto leveling (gilt dann für alle Moonraker-Starts inkl. Mainsail/Orca).
+39
View File
@@ -0,0 +1,39 @@
# Voraussetzungen
Stand **14.10.2026**.
Grotti steuert Drucker, die im eigenen Netz schon Moonraker sprechen. Bewährt ist das auf der Kobra 3 und der Kobra S1, nachdem Rinkhals läuft. Moonraker liegt auf Port 80. Den Aufsatz installiert der Nutzer selbst. Grotti liefert keine Firmware und keine Schraubanleitung.
Verlinkt wird nur die Installationsseite von Rinkhals: <https://github.com/rinkhals-community/Rinkhals>. Die Warnung, die der Hersteller selbst einblendet, bleibt seine.
Andere Drucker nur, wenn Moonraker dort schon erreichbar ist. Dafür gibt es keine eigene Anleitung. Bambu Lab, Prusa und Creality Cloud bleiben bei deren eigenen Programmen.
Der Ablauf Abkühlen, Schieben, Tasten ist für die Kobra 3. Die Kobra S1 ist geschlossen, Abräumen dort ist offen.
Einrichtung der Software (Python, PostgreSQL, Start) steht im [README](../README.md).
## Vor dem ersten Start
Unbeaufsichtigtes Drucken, Hitze und Brandgefahr stehen vor dem ersten Start, nicht im Kleingedruckten. Düse und Bett werden heiß. Den ersten Lauf behält der Nutzer im Blick.
## Grundsatz
**Entwurf.** Kein anwaltlich geprüfter Text. Vor einem Verkauf mit Preis, Abo oder Shop kommen Impressum, Datenschutz und AGB dazu. Diese Seiten nennen keinen Preis.
Grotti flasht nicht, schickt keine Firmware und beschreibt keine Signatur-Umgehung, keinen Root-Exploit und keine Umgehung einer Downgrade-Sperre.
Der Nutzer richtet seinen eigenen Drucker ein, bevor er Grotti nutzt. Verlinkt wird nur die Installationsseite von Rinkhals: <https://github.com/rinkhals-community/Rinkhals>. Die Warnung, die der Hersteller selbst einblendet, bleibt seine.
Anycubic schließt ungenehmigte Änderungen von der Garantie aus. Rinkhals ist ein Community-Aufsatz; die ursprüngliche Software bleibt darunter, der Aufsatz lässt sich wieder entfernen. Ob Anycubic einen Fall annimmt, entscheidet Anycubic.
Creality lässt beim offiziellen Root den Hardware-Service stehen und gibt dafür keine Software-Unterstützung. Fremde Firmware ist von der Garantie ausgenommen (<https://wiki.creality.com/en/warranty-policy>).
Bambu Lab deckt Schäden durch inoffizielle Firmware nicht ab. Der Entwicklermodus ist eine eigene Funktion von Bambu Lab, mit der Warnung, die Bambu Lab selbst einblendet.
Die gesetzliche Mängelhaftung für den Drucker bleibt beim Verkäufer des Druckers. Ein Disclaimer Grotti wischt Verletzung, Vorsatz und grobe Fahrlässigkeit nicht weg.
Unbeaufsichtigtes Drucken, Hitze und Brandgefahr stehen vor dem ersten Start, nicht im Kleingedruckten.
Es gibt kein Auftrags-Flashen und keine vorgeflashten Drucker.
Ausführlich derselbe Hinweis: [Haftung](Haftung.md).
+9
View File
@@ -0,0 +1,9 @@
# Grotti-Wand: Bettnetz
Stand **13.10.2026**.
Die Heatmap im Drucker-Cockpit (unter dem Kamerabild, neben dem 3D-Pfad) zeigt das **aktuelle** Moonraker-Objekt `bed_mesh` je Drucker (`probed_matrix`, sonst `mesh_matrix`), nicht ein Netz zum laufenden G-code.
Drei Drucker mit derselben Datei (z. B. Gridfinity) haben deshalb **drei verschiedene** Netze — eines pro Bett. Die Zeile am 3D-Viewer ist der Auftragspfad (`3D-Pfad · …`), nicht die Netz-Identität.
Grotti merkt sich das zuletzt gelesene Netz im Speicher. Offline oder leeres `bed_mesh` → Anzeige mit Hinweis „offline gespeichert“ und Uhrzeit der letzten erfolgreichen Abfrage. Neu nivellieren am Drucker aktualisiert das Cockpit beim nächsten Poll (~5 s).