# Cabinetto KI-Bot

Cabinetto läuft. Du steuerst Möbel und ganze Küchen über HTTP **auf diesem Port**.
Alle Maße in **Millimetern**.

Gib einem Bot **diese Datei** (Antwort von `GET /`) plus `GET /kataloge` und `GET /zustand`. Dann kann er alles sehen und ändern.

## Zugriff / Token

- **Dieser PC:** `http://127.0.0.1:<Port>/`
- **Aus dem Netz:** `http://<LAN- oder öffentliche IP>:<Port>/`
- Ist in den Stammdaten ein **API-Token** gesetzt, **muss** jeder Request (auch lokal) das Token mitschicken.
- Ohne Token in den Stammdaten: nur localhost, Anfragen von außen → 401.

Token so senden (eins reicht):

```
Authorization: Bearer <Token>
X-Api-Token: <Token>
GET /zustand?token=<Token>
POST-JSON: {"token":"<Token>", "aktion":"..."}
```

Das Token steht in Cabinetto unter Stammdaten → KI. Nicht ins Chat-Log schreiben, wenn vermeidbar.

## Schnellstart

1. Token setzen (Stammdaten → KI → Erzeugen / Speichern).
2. `GET /kataloge` — welche Korpus-/Material-/Türtypen es gibt.
3. `GET /zustand` — das offene Projekt (Knoten, Schränke, Bereiche, Platten, Bohrungen).
4. `POST /` mit JSON `{"aktion":"..."}` — ändern.
5. Nach jeder Änderung kommt `zustand` mit. Bereich-IDs daraus für Böden/Türen verwenden.

## HTTP

| Methode | Pfad | Bedeutung |
|---------|------|-----------|
| GET | `/` `/hilfe` `/ki.md` | dieses Protokoll (Markdown) |
| GET | `/zustand` | Projekt-Snapshot JSON |
| GET | `/kataloge` | Stammdaten-Namen |
| GET | `/ping` | lebendig? |
| POST | `/` | eine Aktion `{"aktion":"..."}` |
| POST | `/stapel` | `{"aktionen":[ {...}, {...} ]}` |

Antwort immer JSON UTF-8:

```json
{"ok":true, "hinweis":"...", "zustand":{}}
```

oder

```json
{"ok":false, "fehler":"...", "hinweis":"GET / für das Protokoll."}
```

Unbekannte Felder werden ignoriert. CORS ist erlaubt. Optional `knapp: true` lässt Platten/Bohrungen weg.

## Koordinaten

- Schrank: Breite = X, Höhe = Y, Tiefe = Z, Ursprung vorn-links-unten im Möbel.
- Projekt-Knoten: `x`/`y` Grundriss (mm), `z` Höhe über Boden, `drehung` Grad um Hochachse (0 = Wandzeile, 90 = Ecke).
- Innenraum: Bereich **1**. Sockel: Bereich **1000**, falls vorhanden.
- Geteilte Felder bekommen neue IDs (siehe `bereiche` im Zustand). Türen/Schubladen/Böden in **Blatt**-Bereiche (`"blatt": true`).

Küche = ein Projekt mit mehreren **Knoten**. Dieselbe Möbel-Definition darf mehrfach stehen (`knoten.neu`).

## Aktionen

### Lesen

- `zustand` — optional `knapp: true`
- `kataloge`
- `hilfe`

### Projekt

- `projekt.neu` — `name`, `kunde`, `sachbearbeiter`, `bemerkung`
- `projekt.laden` — `datei` (Pfad, z. B. `C:\\Küche.bbcx`)
- `projekt.speichern` — `datei` (sonst zuletzt geöffnete)
- `projekt.info` — Name/Kunde setzen

### Schränke

`schrank` darf GUID (`schrankId`), Knotenname, Möbelname oder 1-basierter Index sein. Nur ein Möbel im Projekt: Feld weglassen.

- `schrank.neu` — legt Möbel an
  - `name`, `breite`, `hoehe`, `tiefe` (Default 600×800×560)
  - `korpus` — Name aus `kataloge.korpus` (nicht raten)
  - `material`, `verbindungen`, `hardware` — String oder Liste (unten überschreibt oben)
  - `kanten`, `schubladen`, `griffe`
  - `x`, `y`, `z`, `drehung`
  - `hinzufuegen: true` — in die Küche (Default, wenn schon Möbel da)
  - `ersetzen: true` — neues Projekt nur mit diesem Schrank
- `schrank.masse` — `schrank`, `breite` / `hoehe` / `tiefe` / `sockel`
- `schrank.platzieren` — `schrank`, `x`, `y`, `z`, `drehung`
- `schrank.name` — `schrank`, `name`
- `schrank.loeschen` — `schrank` / `knoten`
- `knoten.neu` — dieselbe Definition nochmal stellen

### Innenausbau

Immer `schrank` + `bereich` (ID aus `zustand.schraenke[].bereiche`).

- `bereich.boeden` — `anzahl` (Default 1; >1 = gleichmäßig)
  - `typ` aus `kataloge.bodenTypen`
  - `lage`: `gleich` | `prozent` | `unten` | `oben`, dazu `wert` (Prozent 0–100 oder mm)
- `bereich.trennwand` — wie Böden, `achse`: `linksrechts` (Default) oder `vornehinten`
  - `typ` aus `trennwandTypen` / `laengsTypen`
- `bereich.tueren` — `typ` aus `tuerTypen`, `anschlag`: `links` | `rechts` | `doppelt`
- `bereich.schubladen` — `anzahl`, `typ` aus `schubladenTypen`, `stapel`: `gleich` | `oben` | `unten`, `sonderhoehe` mm
- `bereich.leeren` — Teiler, Türen, Schubladen in diesem Feld weg
- `tuer.loeschen` / `schublade.loeschen` — `index` (1-basiert) oder `bereich`
- `tuer.anschlag` — `schrank`, `index`, `anschlag`: `links` | `rechts` | `doppelt`
- `tuer.typ` — `schrank`, `index`, `typ` aus `tuerTypen` (z. B. Klapptüre)

### Scharniere / Bohrmuster / Verbindungen

Kataloge unter Data\\ (`{Name}.Connections.XML`, `{Name}.Hardware.XML`). Nach Setzen/Löschen wird standardmäßig auf alle offenen Schränke **angewendet** (`anwenden: false` zum Überspringen).

Teile: `Seite`, `Rueckwand`, `Deckel`, `Boden` oder PartId `02 02 01`, `04 01 08` (Tür-Index).

- `verbindungen.liste` — optional `katalog`
- `verbindung.setzen` — Scharnier/Dübel einer Fuge zuordnen
  - `katalog` (Connections-Datei), `durchgehend`, `gestossen`
  - `partner`: `qualifier` | `beide` | `spiegeln` (Klapptüre oft `beide`)
  - `qualifier`: `beschlag` | `oeffnung` | Zahl
  - `hardware` Name/GUID **oder** `items`: `[{ "hardware":"…", "aufteilung":"…" }]`
- `verbindung.loeschen` — gleiche Schlüssel wie setzen
- `hardware.liste` / `hardware.setzen` / `hardware.loeschen` — Bohrmuster
  - `katalog` (Hardware-Datei), `name`, `guid`, `ordner`
  - `loecher`: `[{ "durchmesser":35, "tiefe":13, "x":0, "y":0, "platte":"durchgehend", "flaeche":"innen", "durchsBrett":false }]`
    `platte`: durchgehend | gestoßen; `flaeche`: innen | aussen | kante
- `aufteilungen.liste` / `aufteilung.setzen` / `aufteilung.loeschen`
  - `typ`: `zentrum` | `innen` | `aussen` | `anfangende`
  - `rand` mm, `split` (einzeln mitte / anfang und ende / …)

Beispiel Klapptüre-Lift auf beide Seiten:

```json
{"aktion":"verbindung.setzen","katalog":"MinimalStandard",
 "durchgehend":"04 01 08","gestossen":"Seite","partner":"beide",
 "hardware":"Hettich Lift"}
```

### Sonst

- `stammdaten.neu` — Kataloge neu auf alle Schränke legen (Bohrungen neu)

## Beispiel: Küchenzeile

```http
POST /stapel
Content-Type: application/json

{
  "aktionen": [
    {"aktion":"projekt.neu","name":"Küche Test","kunde":"Muster"},
    {"aktion":"schrank.neu","name":"US Spüle","breite":900,"hoehe":720,"tiefe":560,
     "korpus":"OliSchrank_externerSockel","x":0,"y":0,"z":0},
    {"aktion":"bereich.schubladen","schrank":"US Spüle","bereich":1,"anzahl":3},
    {"aktion":"schrank.neu","name":"US Herd","breite":600,"hoehe":720,"tiefe":560,
     "x":900,"y":0,"z":0},
    {"aktion":"bereich.tueren","schrank":"US Herd","bereich":1,"anschlag":"links"},
    {"aktion":"schrank.neu","name":"Hängeschrank","breite":900,"hoehe":720,"tiefe":350,
     "korpus":"OliHängeschrank","x":0,"y":0,"z":1400},
    {"aktion":"bereich.boeden","schrank":"Hängeschrank","bereich":1,"anzahl":1,"lage":"prozent","wert":50},
    {"aktion":"bereich.tueren","schrank":"Hängeschrank","bereich":1,"anschlag":"doppelt"}
  ]
}
```

Danach `GET /zustand`. Bei `ok: false` den `fehler` lesen und korrigieren. Korpusnamen immer aus `kataloge` nehmen.

## Tipps für Bots

- Erst `kataloge` + `zustand`, dann bauen.
- Nach Böden/Trennwänden neue Bereich-IDs holen, bevor Türen in Teilfelder kommen.
- Eine fehlgeschlagene Aktion nicht ignorieren.
- Port **0** in den Stammdaten (Register KI) schaltet den Server aus.
- curl lokal: `curl -H "Authorization: Bearer TOKEN" http://127.0.0.1:PORT/zustand`
- curl POST: `curl -H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" -d "{\"aktion\":\"kataloge\"}" http://127.0.0.1:PORT/`
- Von außen dieselbe URL mit der LAN- oder WAN-IP des Rechners. Firewall/Router-Port müssen offen sein.
