Compare commits

..

22 Commits

Author SHA1 Message Date
be110fd766 ci: Docker CLI Installation BusyBox-kompatibel (wget, fixe Versionen) 2026-06-25 12:56:38 +02:00
77fce988d7 ci: Docker CLI als statisches Binary installieren (Alpine-kompatibel) 2026-06-25 12:54:02 +02:00
fe1815c76f ci: Docker CLI im Runner-Container per apt installieren 2026-06-25 12:51:00 +02:00
29a4262a2a ci: Actions auf native Shell-Commands umgestellt (kein Node.js nötig) 2026-06-25 12:46:31 +02:00
e753bcdb03 ci: Builds auf server-runner (Gitea-Server) umgestellt 2026-06-25 12:40:04 +02:00
73ecb1e618 nightly: 0.9.27-nightly2 2026-06-25 12:03:08 +02:00
08d9a7ab0f fix: [spoolman]-Section in config.ini.example nachgetragen (Issue #72) 2026-06-25 11:53:19 +02:00
93f72f0be9 ci: nightly Build nur bei Code-Änderungen oder Cron (nicht bei Docs/Templates) 2026-06-25 10:54:17 +02:00
812936d90d fix(ci): setup-python entfernen — self-hosted Runner nutzt System-Python 2026-06-25 10:53:02 +02:00
326453e2fd README.md aktualisiert 2026-06-24 14:29:15 +02:00
ffd8ed09d5 README.es.md aktualisiert 2026-06-24 14:28:52 +02:00
dc7e92688b README.de.md aktualisiert 2026-06-24 14:28:22 +02:00
3e1ba9df4b docs: Wartungshinweis + CONTRIBUTING.md Link in README.es.md 2026-06-24 13:39:31 +02:00
41f4700b24 docs: Wartungshinweis + CONTRIBUTING.md Link in README (DE+EN) 2026-06-24 13:38:23 +02:00
216b2de2c0 fix: pull_request_template.md ins Root-Verzeichnis (Gitea-Anforderung) 2026-06-24 13:35:48 +02:00
394b0e69ab docs: CONTRIBUTING.md hinzufügen (Fork-Flow, Branch-Modell, Commit-Stil) 2026-06-24 13:32:47 +02:00
877cddb1ba chore: Repo-Struktur vollständig aufsetzen
- Workflows: docker-publish.yml → nightly.yml + release.yml + pr-check.yml
  (nightly: Branch-Push + Cron 02:00, release: v*-Tag, pr-check: lint+tests)
- Issue-Templates: bug_report.md + feature_request.md (englisch)
- PR-Template: pull_request_template.md (englisch)
- Claude-Agenten: reviewer, changelog, test-writer, nightly-prep,
  docker-check, moonraker-debug + settings.json
- agents.md: Agenten-Übersicht im Repo-Root
- .gitignore: .runner-token + secrets/ ausgeschlossen
2026-06-24 13:21:29 +02:00
c9043e9630 ci: nightly-Datum-Tag hinzufügen (nightly-YYYYMMDD) 2026-06-24 13:10:55 +02:00
6165a7f62a ci: retrigger workflow test 2026-06-24 13:09:26 +02:00
fa8e0c1491 ci: test workflow trigger auf nightly-Branch 2026-06-24 13:08:49 +02:00
282c02ae0a chore: .runner-token zu .gitignore hinzufügen 2026-06-24 13:01:46 +02:00
72f77d92af feat: Integrationen-Tab + KobraX Full Stack Compose + CI-Workflow
- Settings-Tab "Integrationen": Spoolman URL/Sync-Rate konfigurierbar,
  Obico Read-only Hinweis auf moonraker-obico.cfg
- docker-compose-KX.yml: Portainer-kompatibler Full Stack (KX-Bridge +
  Obico Self-Hosted + Spoolman + moonraker-obico Plugin)
- moonraker-obico.cfg.example: Verbindungsvorlage für Obico-Integration
- .gitea/workflows/docker-publish.yml: Push auf nightly → :nightly Image,
  v*-Tag → :latest + :<VERSION>
2026-06-24 12:58:46 +02:00
35 changed files with 1018 additions and 95 deletions

View File

@@ -0,0 +1,31 @@
---
name: KX-Bridge Changelog
description: Generiert einen CHANGELOG.md Eintrag aus Git-Commits seit dem letzten Tag.
tools:
- run_command
- read_file
- write_file
---
Du generierst CHANGELOG.md Einträge für KX-Bridge.
Vorgehen:
1. Führe aus: `git log $(git describe --tags --abbrev=0)..HEAD --oneline`
2. Gruppiere Commits nach Präfix: feat → Neu, fix → Behoben, chore/refactor/docs → Geändert
3. Frage nach der Versionsnummer (SemVer: feat→MINOR, fix→PATCH, breaking→MAJOR)
4. Schreibe den Abschnitt im Format:
```
## [VERSION] - DATUM
### Neu
- ...
### Behoben
- ...
### Geändert
- ...
```
5. Füge den Abschnitt am Anfang der bestehenden CHANGELOG.md ein, ohne vorhandene Einträge zu ändern.

View File

@@ -0,0 +1,32 @@
---
name: KX-Bridge Docker Check
description: Prüft Dockerfile, docker-compose und das gebaute Image auf häufige Probleme.
tools:
- read_file
- run_command
- search_files
---
Du prüfst die Docker-Konfiguration von KX-Bridge.
**Dockerfile:**
- Base-Image aktuell? (`python:3.11-slim` oder neuer)
- `.dockerignore` vorhanden und vollständig?
- Keine Secrets oder Zertifikate im Image (`anycubic_slicer.crt/.key` darf NICHT eingebettet sein)
- Healthcheck vorhanden?
- Kein `COPY . .` ohne `.dockerignore`
**docker-compose.yml:**
- Port 7125 korrekt gemappt
- Config-Volume gemountet (`/app/config`)
- `restart: unless-stopped` gesetzt
- Logging-Limits konfiguriert (`max-size`, `max-file`)
**Image-Check (falls lokal vorhanden):**
```bash
docker image inspect gitea.it-drui.de/viewit/kx-bridge:nightly
```
- Image-Größe sinnvoll (< 500MB)?
- Keine privaten Keys eingebettet: `docker history --no-trunc`
Berichte nach Schweregrad: Kritisch / Warnung / Hinweis.

View File

@@ -0,0 +1,23 @@
---
name: KX-Bridge Moonraker Debug
description: Analysiert Moonraker/Klipper Logs und KX-Bridge Ausgaben auf Fehlerursachen.
tools:
- read_file
- search_files
---
Du analysierst Logs für KX-Bridge im Kontext Moonraker/Klipper/AFC.
**Bekannte Problemquellen:**
- AFC lane_data Indizierung: korrekt ist `lane1``lane4` (flat), nicht Slot 03
- `filament_id` muss als String übertragen werden, nicht als Integer
- Moonraker WebSocket trennt bei Inaktivität → keep-alive prüfen
- OrcaSlicer sendet Bambu MQTT Format → KX-Bridge muss übersetzen
- ACE 2 Pro meldet Fehler wenn Lane leer aber als belegt markiert ist
- MQTT mTLS: Zertifikat muss neben dem Binary liegen (`anycubic_slicer.crt/.key`)
**Bei einem Log:**
1. Identifiziere den **ersten** Fehler (nicht den letzten Symptom)
2. Zeige den relevanten Log-Kontext (±10 Zeilen um den Fehler)
3. Nenne die wahrscheinliche Ursache
4. Schlage einen konkreten Fix vor (Datei + Funktion wenn möglich)

View File

@@ -0,0 +1,25 @@
---
name: KX-Bridge Nightly Prep
description: Bereitet den PR von nightly nach main vor. Prüft ob alle Voraussetzungen für ein Stable Release erfüllt sind.
tools:
- run_command
- read_file
---
Du bereitest einen nightly → main Merge für KX-Bridge vor.
Führe folgende Checks aus und berichte:
1. `git log main..nightly --oneline` → alle Commits die noch nicht in main sind
2. `git diff main..nightly -- CHANGELOG.md` → ist CHANGELOG.md für alle Änderungen aktualisiert?
3. Prüfe ob `tests/` alle geänderten Module abdeckt
4. Prüfe ob Dockerfile ein aktuelles Base-Image verwendet
5. Schlage eine SemVer-Versionsnummer vor:
- `feat:` Commits → MINOR erhöhen
- `fix:` Commits → PATCH erhöhen
- Breaking Change im Commit-Body → MAJOR erhöhen
Abschlussbericht:
- ✅ Bereit für Release
- ⚠️ Offen: [Liste]
- ❌ Blockiert durch: [Grund]

View File

@@ -0,0 +1,24 @@
---
name: KX-Bridge Reviewer
description: Reviewt geänderte Dateien vor einem PR auf nightly. Prüft Logik, Fehlerbehandlung, Moonraker-Kompatibilität und Stil.
tools:
- read_file
- list_directory
- search_files
---
Du bist Code-Reviewer für KX-Bridge — eine Python-Bridge zwischen OrcaSlicer und Moonraker/Klipper für den Anycubic Kobra X.
Beim Review prüfst du:
- Korrekte Fehlerbehandlung bei Moonraker HTTP/MQTT Calls (keine unbehandelten Exceptions)
- Keine hardcodierten IPs oder Ports (müssen aus config.ini kommen)
- Thread-Sicherheit bei parallelen Moonraker-Abfragen (asyncio korrekt verwendet)
- AFC lane_data Struktur: flache Indizierung lane1lane4, kein Slot-Mapping 03
- Kein `print()` statt `logging` (außer in CLI-Hilfsfunktionen)
- Typ-Annotationen vorhanden, Python 3.8+ kompatibel (kein `X | Y` Syntax)
- Tests für neue öffentliche Funktionen vorhanden
Ausgabeformat:
1. **Kritische Fehler** — blockieren den Merge
2. **Warnungen** — sollten vor Merge behoben werden
3. **Hinweise** — optional, für zukünftige Verbesserungen

View File

@@ -0,0 +1,26 @@
---
name: KX-Bridge Test Writer
description: Leitet pytest-Tests aus geänderten oder neuen Python-Dateien ab.
tools:
- read_file
- write_file
- list_directory
- search_files
---
Du schreibst pytest-Tests für KX-Bridge.
Kontext:
- Moonraker API läuft auf Port 7125 (HTTP + WebSocket)
- AFC lane_data: flache Indizierung lane1lane4
- Externe HTTP-Calls zu Moonraker werden mit `unittest.mock` gemockt
- Python 3.8+ Kompatibilität (kein `X | Y` Union-Syntax)
Für jede zu testende Funktion schreibst du:
1. Happy Path (Normalfall mit validen Eingaben)
2. Fehlerfall (Moonraker nicht erreichbar, Timeout, falsche Antwort)
3. Grenzwerte (leere lane_data, ungültige filament_id, None-Werte)
Dateiname: `tests/test_<modulname>.py`
Verwende pytest-Fixtures für Moonraker-Mock-Responses.
Keine echten Netzwerkaufrufe in Tests.

12
.claude/settings.json Normal file
View File

@@ -0,0 +1,12 @@
{
"project": "KX-Bridge",
"language": "de",
"defaultAgent": "reviewer",
"context": {
"repoBase": "gitea.it-drui.de/viewit/KX-Bridge-Release",
"defaultBranch": "nightly",
"stableBranch": "main",
"registry": "gitea.it-drui.de/viewit/kx-bridge",
"moonrakerPort": 7125
}
}

View File

@@ -1,30 +0,0 @@
# EditorConfig helps maintain consistent coding styles across all files
# https://editorconfig.org
root = true
# Unix-style newlines, UTF-8 encoding for all files
[*]
end_of_line = lf
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
# Python: 4 spaces (PEP 8)
[*.py]
indent_style = space
indent_size = 4
# JavaScript/JSON/YAML: 2 spaces (common web standard)
[*.{js,json,yml,yaml}]
indent_style = space
indent_size = 2
# HTML/CSS: 2 spaces
[*.{html,css}]
indent_style = space
indent_size = 2
# Markdown: preserve formatting
[*.md]
trim_trailing_whitespace = false

View File

@@ -0,0 +1,29 @@
---
name: Bug Report
about: Report a bug in KX-Bridge
labels: bug
---
## Description
<!-- What is happening? -->
## Steps to Reproduce
1.
2.
3.
## Expected Behavior
## Actual Behavior
## Environment
- KX-Bridge Version:
- OrcaSlicer Version:
- Moonraker/Klipper Version:
- Operating System:
- Installation: Docker / Binary
## Logs
```
<!-- docker logs kx-bridge --tail 50 -->
```

View File

@@ -0,0 +1,14 @@
---
name: Feature Request
about: Suggest a new feature or improvement
labels: enhancement
---
## Description
<!-- What should be added or improved? -->
## Motivation
<!-- Why is this useful? What problem does it solve? -->
## Proposed Implementation
<!-- Optional: How could this be implemented technically? -->

View File

@@ -0,0 +1,21 @@
## Description
<!-- What does this PR change? -->
## Related Issue
Closes #
## Type
- [ ] Bug fix
- [ ] Feature
- [ ] Documentation
- [ ] Refactoring
## Tested with
- OrcaSlicer Version:
- Printer:
- Moonraker/Klipper Version:
## Checklist
- [ ] Tests added/updated
- [ ] CHANGELOG.md updated
- [ ] No debug code included

View File

@@ -0,0 +1,88 @@
name: Nightly Build
on:
push:
branches:
- nightly
paths:
- '**.py'
- 'Dockerfile'
- 'requirements.txt'
- 'web/**'
- 'data/**'
schedule:
- cron: '0 2 * * *'
workflow_dispatch:
jobs:
build:
runs-on: server-runner
steps:
- name: Checkout
run: |
if [ -d .git ]; then
git fetch origin nightly
git reset --hard origin/nightly
git clean -fd
else
git clone --depth=1 --branch nightly https://gitea.it-drui.de/viewit/KX-Bridge-Release.git .
fi
- name: Install Docker CLI
run: |
if ! command -v docker >/dev/null 2>&1; then
ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then
DARCH="x86_64"
BARCH="amd64"
else
DARCH="aarch64"
BARCH="arm64"
fi
wget -qO- "https://download.docker.com/linux/static/stable/${DARCH}/docker-27.5.1.tgz" \
| tar xz --strip-components=1 -C /usr/local/bin docker/docker
chmod +x /usr/local/bin/docker
mkdir -p /usr/local/lib/docker/cli-plugins
wget -qO /usr/local/lib/docker/cli-plugins/docker-buildx \
"https://github.com/docker/buildx/releases/download/v0.23.0/buildx-v0.23.0.linux-${BARCH}"
chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx
fi
docker version --format '{{.Client.Version}}'
- name: Set up QEMU
run: |
docker run --rm --privileged tonistiigi/binfmt:latest --install all
- name: Set up buildx
run: |
docker buildx inspect kxbuilder 2>/dev/null || \
docker buildx create --name kxbuilder --use
docker buildx use kxbuilder
- name: Login to Gitea registry
run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | \
docker login gitea.it-drui.de -u "${{ secrets.REGISTRY_USER }}" --password-stdin
- name: Build & push (amd64 + arm64)
run: |
DATE=$(date +%Y%m%d)
docker buildx build \
--platform linux/amd64,linux/arm64 \
--push \
--provenance=false \
--no-cache \
-t "gitea.it-drui.de/viewit/kx-bridge:nightly" \
-t "gitea.it-drui.de/viewit/kx-bridge:nightly-$DATE" \
.
- name: Set nightly tag
env:
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
DATE=$(date +%Y%m%d)
TAG="nightly-$DATE"
git config user.name "gitea-actions"
git config user.email "actions@it-drui.de"
git tag -f "$TAG"
git push https://gitea-actions:${GITEA_TOKEN}@gitea.it-drui.de/viewit/KX-Bridge-Release.git "$TAG" --force

View File

@@ -0,0 +1,34 @@
name: PR Check
on:
pull_request:
branches:
- nightly
jobs:
lint-and-test:
runs-on: server-runner
steps:
- name: Checkout
run: |
if [ -d .git ]; then
git fetch origin
git reset --hard origin/nightly
git clean -fd
else
git clone --depth=1 --branch nightly https://gitea.it-drui.de/viewit/KX-Bridge-Release.git .
fi
- name: Dependencies installieren
run: pip3 install -r requirements.txt
- name: Lint
run: |
pip3 install flake8
flake8 *.py --max-line-length=120 --extend-ignore=E501
- name: Tests
run: |
pip3 install pytest
pytest tests/ -v
if: ${{ hashFiles('tests/') != '' }}

View File

@@ -0,0 +1,87 @@
name: Stable Release
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
jobs:
release:
runs-on: server-runner
steps:
- name: Checkout
run: |
TAG="${GITHUB_REF#refs/tags/}"
if [ -d .git ]; then
git fetch --tags origin
git checkout "$TAG"
git clean -fd
else
git clone --depth=1 --branch "$TAG" https://gitea.it-drui.de/viewit/KX-Bridge-Release.git .
fi
- name: Install Docker CLI
run: |
if ! command -v docker >/dev/null 2>&1; then
ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then
DARCH="x86_64"
BARCH="amd64"
else
DARCH="aarch64"
BARCH="arm64"
fi
wget -qO- "https://download.docker.com/linux/static/stable/${DARCH}/docker-27.5.1.tgz" \
| tar xz --strip-components=1 -C /usr/local/bin docker/docker
chmod +x /usr/local/bin/docker
mkdir -p /usr/local/lib/docker/cli-plugins
wget -qO /usr/local/lib/docker/cli-plugins/docker-buildx \
"https://github.com/docker/buildx/releases/download/v0.23.0/buildx-v0.23.0.linux-${BARCH}"
chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx
fi
docker version --format '{{.Client.Version}}'
- name: Set up QEMU
run: |
docker run --rm --privileged tonistiigi/binfmt:latest --install all
- name: Set up buildx
run: |
docker buildx inspect kxbuilder 2>/dev/null || \
docker buildx create --name kxbuilder --use
docker buildx use kxbuilder
- name: Login to Gitea registry
run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | \
docker login gitea.it-drui.de -u "${{ secrets.REGISTRY_USER }}" --password-stdin
- name: Build & push (amd64 + arm64)
run: |
VERSION="${GITHUB_REF#refs/tags/}"
docker buildx build \
--platform linux/amd64,linux/arm64 \
--push \
--provenance=false \
--no-cache \
-t "gitea.it-drui.de/viewit/kx-bridge:latest" \
-t "gitea.it-drui.de/viewit/kx-bridge:${VERSION}" \
.
- name: Create Gitea Release
env:
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
VERSION="${GITHUB_REF#refs/tags/}"
CHANGELOG=$(awk "/^## \[${VERSION}\]/{found=1; next} found && /^## \[/{exit} found{print}" CHANGELOG.md || echo "")
curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases" \
-d "{
\"tag_name\": \"${VERSION}\",
\"name\": \"KX-Bridge ${VERSION}\",
\"body\": $(echo "$CHANGELOG" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))'),
\"draft\": false,
\"prerelease\": false
}"

7
.gitignore vendored
View File

@@ -8,8 +8,6 @@ releases/*/kx-bridge
releases/*/extract_credentials
releases/*/extract_credentials.exe
node_modules/*
!kx-bridge.spec
# Laufzeit-Daten und Drucker-Credentials — nie committen
@@ -19,3 +17,8 @@ config/*.ini
data/
!data/orca_filaments.json
# Sensitive files — never commit
.runner-token
secrets/
*.token

102
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,102 @@
# Contributing to KX-Bridge
Thanks for taking the time to contribute! Here's everything you need to know.
---
## How to report a bug or request a feature
Use the issue tracker:
- **Bug:** [New Bug Report](https://gitea.it-drui.de/viewit/KX-Bridge-Release/issues/new?template=bug_report.md)
- **Feature:** [New Feature Request](https://gitea.it-drui.de/viewit/KX-Bridge-Release/issues/new?template=feature_request.md)
Please fill in the template — especially the **KX-Bridge version** and **logs**.
Issues without version info are hard to debug.
---
## How to submit a Pull Request
### 1. Fork the repository
Click **Fork** at the top of this page.
You now have your own copy at `gitea.it-drui.de/your-username/KX-Bridge-Release`.
### 2. Clone your fork
```bash
git clone https://gitea.it-drui.de/your-username/KX-Bridge-Release.git
cd KX-Bridge-Release
```
### 3. Create a branch
Always branch off `nightly`:
```bash
git checkout nightly
git checkout -b feature/my-feature # or fix/my-fix
```
### 4. Make your changes
- Test your changes locally with Docker:
```bash
docker build -t kx-bridge:dev .
docker run -p 7125:7125 -v ./config:/app/config kx-bridge:dev
```
- No debug `print()` statements — use `logging`
- Keep commits focused; one thing per commit
### 5. Push and open a PR
```bash
git push origin feature/my-feature
```
Gitea will show a banner — click **"Create Pull Request"**.
The PR template will be pre-filled. Set the target branch to **`nightly`**.
---
## Branch model
```
main ← stable releases only (merged by maintainer)
nightly ← integration branch — PRs go here
feature/* ← your feature branch (in your fork)
fix/* ← your bugfix branch (in your fork)
```
Your PR always targets `nightly`. The maintainer periodically merges `nightly → main` for a new stable release.
---
## Commit style
Use conventional commit prefixes:
| Prefix | When |
|---|---|
| `feat:` | new feature |
| `fix:` | bug fix |
| `docs:` | documentation only |
| `chore:` | maintenance, dependencies |
| `refactor:` | code change without new feature or fix |
Example: `fix: prevent crash when printer is offline during startup`
---
## Language
- **Code and comments:** English
- **Issue comments:** match the language of the issue (if someone writes in German, reply in German)
- **Commit messages:** English
---
## Questions?
Open a [Discussion](https://gitea.it-drui.de/viewit/KX-Bridge-Release/issues) or leave a comment on the relevant issue.

View File

@@ -14,6 +14,15 @@ ninguna está oficialmente probada ni soportada. Se agradece el feedback.</sub>
<sub>🇬🇧 <a href="README.md">English version</a> · 🇩🇪 <a href="README.de.md">Deutsche Version</a></sub>
</div>
> [!CAUTION]
> **Trabajos de mantenimiento en curso esta semana** — Estamos reestructurando el repositorio (modelo de ramas, flujos CI, proceso de contribución). Es posible que notes cambios en los nombres de ramas, plantillas de PR y la forma en que se publican las versiones. Pedimos disculpas por los inconvenientes. El manejo, el flujo de trabajo y la mantenibilidad a largo plazo mejorarán significativamente como resultado.
>
> 👉 ¿Quieres contribuir? Por favor lee primero [CONTRIBUTING.md](CONTRIBUTING.md).
<div align="center">
<br>
[![Ko-fi](https://img.shields.io/badge/Ko--fi-Apoya%20este%20proyecto-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/viewitde)
@@ -130,11 +139,6 @@ Impresora → Tipo de conexión **Moonraker** → Host: `http://IP-DEL-PUENTE:71
---
## 📺 Vídeo tutorial
[![Configuración y uso de KX-Bridge](https://img.youtube.com/vi/1Ql4wfH27fM/hqdefault.jpg)](https://www.youtube.com/watch?v=1Ql4wfH27fM)
---
## 🎨 Slicer recomendado

View File

@@ -1 +1 @@
0.9.27-nightly1
0.9.27-nightly2

31
agents.md Normal file
View File

@@ -0,0 +1,31 @@
# KX-Bridge Claude Agents
## Available Agents
| Agent | File | When to use |
|---|---|---|
| Reviewer | `.claude/agents/reviewer.md` | Before every PR — checks logic, error handling, Moonraker compatibility |
| Changelog | `.claude/agents/changelog.md` | After merge to nightly — generates CHANGELOG.md entry from commits |
| Test Writer | `.claude/agents/test-writer.md` | When adding new functions — derives pytest tests |
| Nightly Prep | `.claude/agents/nightly-prep.md` | Before a release — checks readiness of nightly → main merge |
| Docker Check | `.claude/agents/docker-check.md` | Before image push — validates Dockerfile and compose config |
| Moonraker Debug | `.claude/agents/moonraker-debug.md` | On runtime errors — analyzes Moonraker/Klipper logs |
## Usage
In VS Code with Claude Code extension:
```
@reviewer → code review of current changes
@changelog → generate CHANGELOG entry
@test-writer → write tests for changed files
@nightly-prep → check release readiness
@docker-check → validate Docker config
@moonraker-debug → analyze logs
```
## Context
- Moonraker API: Port 7125
- AFC lane_data: flat indexing lane1lane4
- Registry: `gitea.it-drui.de/viewit/kx-bridge`
- Default PR target: `nightly`

View File

@@ -31,6 +31,88 @@ default_ams_slot = auto
# Auto-Leveling vor jedem Druck (1 = an, 0 = aus)
auto_leveling = 1
# Kamera-Stream bei Druckstart automatisch einschalten (1 = an, 0 = aus)
camera_on_print = 0
# Warnung vor Druck von Web-Uploads (1 = an, 0 = aus)
web_upload_warning = 1
# Nach Upload: Filament/Color-Selector automatisch öffnen (1 = an, 0 = aus)
print_start_dialog = 1
# ─── Filament-Profile pro AMS-Slot (optional) ────────────────────────────────
# Beim Slicer-Sync nimmt OrcaSlicer per Default immer "Generic PLA/PETG/...".
# Mit diesen Mappings sendet die Bridge die konkrete Orca-Filament-ID +
# Vendor mit (Anzeige im Slicer dann z.B. "PolyTerra PLA — Polymaker" statt
# nur "Generic PLA"). Mapping wird über die Web-UI gepflegt.
# Beispiel:
# [filament_profiles]
# slot_0_id = OGFL01
# slot_0_vendor = Polymaker
# slot_1_id = OGFG23
# slot_1_vendor = Polymaker
[bridge]
# Poll-Intervall in Sekunden
poll_interval = 3
# ─── Multi-Printer (optional) ──────────────────────────────────────────────────
# Mehrere Drucker können als [printer_1], [printer_2], … definiert werden.
# Jede Bridge-Instanz verbindet sich mit einem Drucker (je eigener Port).
# bridge_url zeigt auf die jeweilige Bridge-Instanz (für den /kx/printers-Endpunkt).
# Die [connection]-Sektion wird weiterhin als Fallback für diese Instanz verwendet.
#
# Beispiel:
# [printer_1]
# name = Kobra X Links
# bridge_url = http://192.168.178.95:7125
# printer_ip = 192.168.178.95
# mqtt_port = 9883
# username = userXXXXXXXXXX
# password = XXXXXXXXXXXXXXX
# device_id = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# mode_id = 20030
#
# [printer_2]
# name = Kobra X Rechts
# bridge_url = http://192.168.178.96:7125
# printer_ip = 192.168.178.96
# mqtt_port = 9883
# username = userYYYYYYYYYY
# password = YYYYYYYYYYYYYYY
# device_id = yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
# mode_id = 20030
[ace_dry_presets]
# Vordefinierte Dry-Set Presets (Temp in °C, Dauer in Sekunden)
pla_temp = 45
pla_duration_sec = 14400
pla_plus_temp = 45
pla_plus_duration_sec = 14400
petg_temp = 50
petg_duration_sec = 14400
tpu_temp = 55
tpu_duration_sec = 14400
abs_asa_temp = 45
abs_asa_duration_sec = 28800
pa_pc_temp = 55
pa_pc_duration_sec = 43200
# Custom Presets (Name + Temp + Dauer)
custom_1_name = Custom 1
custom_1_temp = 45
custom_1_duration_sec = 14400
custom_2_name = Custom 2
custom_2_temp = 45
custom_2_duration_sec = 14400
custom_3_name = Custom 3
custom_3_temp = 45
custom_3_duration_sec = 14400
[spoolman]
# URL der Spoolman-Instanz (leer lassen um Spoolman zu deaktivieren)
# server = http://192.168.x.x:7912
# Wie oft (Sekunden) der Filamentverbrauch während des Drucks gemeldet wird
# (0 = nur beim Druckende)
# sync_rate = 0

210
docker-compose-KX.yml Normal file
View File

@@ -0,0 +1,210 @@
# KobraX Full Stack — KX-Bridge + Obico Self-Hosted + Spoolman
#
# Für Portainer: Stack → Add Stack → Upload → diese Datei wählen
#
# Voraussetzung: Obico-Images einmalig in Gitea-Registry pushen:
# docker tag obico-server-web:latest gitea.it-drui.de/viewit/obico-web:latest
# docker tag obico-server-ml_api:latest gitea.it-drui.de/viewit/obico-ml:latest
# docker tag obico-server-tasks:latest gitea.it-drui.de/viewit/obico-tasks:latest
# docker push gitea.it-drui.de/viewit/obico-web:latest
# docker push gitea.it-drui.de/viewit/obico-ml:latest
# docker push gitea.it-drui.de/viewit/obico-tasks:latest
#
# Persistente Daten: /mnt/dockerdata/KobraXStack/<service>/
#
# Ports:
# 7125 — KX-Bridge (Moonraker-API)
# 3334 — Obico (Web-UI)
# 7912 — Spoolman (Web-UI)
#
# Obico Admin-Account nach dem ersten Start:
# docker exec obico-web python manage.py createsuperuser
x-obico-base: &obico-base
restart: unless-stopped
volumes:
- /mnt/dockerdata/KobraXStack/obico/data:/data
- /mnt/dockerdata/KobraXStack/obico/frontend:/frontend
depends_on:
- obico-redis
environment:
DEBUG: "False"
REDIS_URL: "redis://obico-redis:6379"
DATABASE_URL: "sqlite:////data/db.sqlite3"
INTERNAL_MEDIA_HOST: "http://obico-web:3334"
ML_API_HOST: "http://obico-ml:3333"
ACCOUNT_ALLOW_SIGN_UP: "False"
SITE_USES_HTTPS: "False"
SITE_IS_PUBLIC: "False"
DJANGO_SECRET_KEY: "change-me-to-a-random-secret-key-before-use"
WEBPACK_LOADER_ENABLED: "False"
networks:
- kobrax-stack
services:
# ── KX-Bridge ───────────────────────────────────────────────
kx-bridge:
image: gitea.it-drui.de/viewit/kx-bridge:latest
container_name: kx-bridge
restart: unless-stopped
ports:
- "7125:7125"
volumes:
- /mnt/dockerdata/KobraXStack/kx-bridge/config:/app/config
- /mnt/dockerdata/KobraXStack/kx-bridge/data:/app/data
networks:
- kobrax-stack
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── Spoolman ────────────────────────────────────────────────
spoolman:
image: ghcr.io/donkie/spoolman:latest
container_name: spoolman
restart: unless-stopped
ports:
- "7912:8000"
volumes:
- /mnt/dockerdata/KobraXStack/spoolman:/home/app/.local/share/spoolman
networks:
- kobrax-stack
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── Obico Redis ─────────────────────────────────────────────
obico-redis:
image: redis:7.2-alpine
container_name: obico-redis
restart: unless-stopped
volumes:
- /mnt/dockerdata/KobraXStack/obico/redis:/data
networks:
- kobrax-stack
healthcheck:
test: ["CMD", "redis-cli", "ping"]
start_period: 10s
interval: 15s
timeout: 5s
retries: 10
logging:
driver: json-file
options:
max-size: "5m"
max-file: "2"
# ── Obico ML API ────────────────────────────────────────────
obico-ml:
image: gitea.it-drui.de/viewit/obico-ml:latest
container_name: obico-ml
restart: unless-stopped
command: bash -c "gunicorn --bind 0.0.0.0:3333 --workers 1 wsgi"
working_dir: /app
environment:
DEBUG: "False"
FLASK_APP: "server.py"
networks:
- kobrax-stack
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:3333/hc/ || exit 1"]
start_period: 30s
interval: 30s
timeout: 10s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── Obico Web ───────────────────────────────────────────────
obico-web:
<<: *obico-base
image: gitea.it-drui.de/viewit/obico-web:latest
container_name: obico-web
ports:
- "3334:3334"
depends_on:
- obico-ml
- obico-redis
command: >
sh -c 'python manage.py migrate &&
python manage.py shell -c "from django.contrib.sites.models import Site; s=Site.objects.first(); s.domain=\"192.168.178.204:3334\"; s.name=\"Obico\"; s.save()" &&
python manage.py collectstatic --noinput &&
daphne -b 0.0.0.0 -p 3334 config.routing:application'
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:3334/hc/ || exit 1"]
start_period: 60s
interval: 90s
timeout: 20s
retries: 3
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── Obico Tasks (Celery) ────────────────────────────────────
obico-tasks:
<<: *obico-base
image: gitea.it-drui.de/viewit/obico-tasks:latest
container_name: obico-tasks
command: sh -c "celery -A config worker --beat -l info -c 2 -Q realtime,celery"
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# ── moonraker-obico Plugin ──────────────────────────────────
# Verbindet KX-Bridge mit dem Obico-Server (Spaghetti-Detektion, Remote-UI)
# Voraussetzung: /mnt/dockerdata/KobraXStack/moonraker-obico/moonraker-obico.cfg
# muss existieren und einen gültigen auth_token enthalten.
#
# Token holen (nach erstem obico-web Start):
# docker exec obico-web python manage.py shell -c "
# from app.models import OneTimeVerificationCode, User
# from django.utils import timezone; from datetime import timedelta; import random
# u = User.objects.first()
# c = OneTimeVerificationCode.objects.create(user=u, code='%06d' % random.randint(100000,999999), expired_at=timezone.now()+timedelta(hours=2))
# print('CODE:', c.code)"
# curl -X POST 'http://localhost:3334/api/v1/octo/verify/?code=<CODE>'
# → printer.auth_token aus der Antwort in die cfg eintragen
moonraker-obico:
image: gitea.it-drui.de/viewit/moonraker-obico:latest
container_name: moonraker-obico
restart: unless-stopped
network_mode: host
volumes:
- /mnt/dockerdata/KobraXStack/moonraker-obico:/opt/printer_data/config
- /mnt/dockerdata/KobraXStack/moonraker-obico/logs:/opt/printer_data/logs
command: ["-c", "/opt/printer_data/config/moonraker-obico.cfg"]
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
networks:
kobrax-stack:
driver: bridge
# Verzeichnisse müssen auf dem Host existieren:
# mkdir -p /mnt/dockerdata/KobraXStack/kx-bridge/config \
# /mnt/dockerdata/KobraXStack/kx-bridge/data \
# /mnt/dockerdata/KobraXStack/spoolman \
# /mnt/dockerdata/KobraXStack/obico/data \
# /mnt/dockerdata/KobraXStack/obico/frontend \
# /mnt/dockerdata/KobraXStack/obico/redis \
# /mnt/dockerdata/KobraXStack/moonraker-obico/logs
# Spoolman benötigt UID/GID 1000:
# sudo chown -R 1000:1000 /mnt/dockerdata/KobraXStack/spoolman
#
# moonraker-obico Config anlegen (auth_token nach Obico-Setup eintragen):
# cp /path/to/moonraker-obico.cfg.example /mnt/dockerdata/KobraXStack/moonraker-obico/moonraker-obico.cfg

View File

@@ -4218,6 +4218,8 @@ class KobraXBridge:
"filament_profiles": {str(k): v for k, v in self._filament_profiles.items()},
"visible_vendors": self._visible_vendors,
"ace_dry_presets": self._ace_dry_presets,
"spoolman_server": getattr(self._args, "spoolman_server", "") or "",
"spoolman_sync_rate": getattr(self._args, "spoolman_sync_rate", 0),
})
async def handle_api_settings_post(self, request):
@@ -4232,7 +4234,7 @@ class KobraXBridge:
cfg.read(config_path, encoding="utf-8")
# Sections sicherstellen
for section in ("connection", "print", "bridge", "ace_dry_presets"):
for section in ("connection", "print", "bridge", "ace_dry_presets", "spoolman"):
if not cfg.has_section(section):
cfg.add_section(section)
@@ -4262,6 +4264,16 @@ class KobraXBridge:
elif cfg.has_option("bridge", "printer_name"):
cfg.remove_option("bridge", "printer_name")
# Spoolman
if "spoolman_server" in data:
cfg.set("spoolman", "server", str(data["spoolman_server"]).strip())
if "spoolman_sync_rate" in data:
try:
sr = max(0, int(data["spoolman_sync_rate"]))
except (TypeError, ValueError):
sr = 30
cfg.set("spoolman", "sync_rate", str(sr))
incoming_presets = data.get("ace_dry_presets") if isinstance(data, dict) else None
presets = self._sanitize_ace_dry_presets(incoming_presets if isinstance(incoming_presets, dict) else self._ace_dry_presets)
for key, val in presets.items():

View File

@@ -0,0 +1,18 @@
[server]
url = http://127.0.0.1:3334
auth_token = REPLACE_ME
sentry_opt = out
[moonraker]
host = 127.0.0.1
port = 7125
[webcam]
disable_video_streaming = False
snapshot_url = http://127.0.0.1:7125/api/camera/snapshot
stream_url = http://127.0.0.1:7125/api/camera/stream
target_fps = 5
[logging]
path = /opt/printer_data/logs/moonraker-obico.log
level = INFO

31
package-lock.json generated
View File

@@ -1,31 +0,0 @@
{
"name": "kx-bridge",
"version": "0.9.27",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "kx-bridge",
"version": "0.9.27",
"devDependencies": {
"prettier": "^3.0.0"
}
},
"node_modules/prettier": {
"version": "3.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz",
"integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==",
"dev": true,
"license": "MIT",
"bin": {
"prettier": "bin/prettier.cjs"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
}
}
}

View File

@@ -1,23 +0,0 @@
{
"name": "kx-bridge",
"version": "0.9.27",
"description": "Moonraker-compatible bridge for Anycubic Kobra X",
"type": "module",
"devDependencies": {
"prettier": "^3.0.0"
},
"scripts": {
"format:js": "prettier --write 'web/**/*.js' '*.js'",
"format:json": "prettier --write '*.json' 'web/**/*.json'",
"format:web": "prettier --write 'web/**/*.{js,json,html,css,yml,yaml,md}'",
"format": "npm run format:web"
},
"prettier": {
"semi": true,
"singleQuote": false,
"trailingComma": "es5",
"printWidth": 100,
"tabWidth": 2,
"useTabs": false
}
}

21
pull_request_template.md Normal file
View File

@@ -0,0 +1,21 @@
## Description
<!-- What does this PR change? -->
## Related Issue
Closes #
## Type
- [ ] Bug fix
- [ ] Feature
- [ ] Documentation
- [ ] Refactoring
## Tested with
- OrcaSlicer Version:
- Printer:
- Moonraker/Klipper Version:
## Checklist
- [ ] Tests added/updated
- [ ] CHANGELOG.md updated
- [ ] No debug code included

View File

@@ -1,2 +0,0 @@
# Code formatting
black>=24.1.0

View File

@@ -53,8 +53,19 @@ function _loadSpoolmanStatus(){
fetch(_apiUrl('/kx/spoolman/status')).then(function(r){return r.json();}).then(function(d){
_spoolmanStatus=d;
_slotSpoolMap=d.slot_spools||{};
_updateSpoolmanStatusDot();
}).catch(function(){});
}
function _updateSpoolmanStatusDot(){
var dot=document.getElementById('spoolman-status-dot');
var lbl=document.getElementById('spoolman-status-lbl');
if(!dot||!lbl)return;
if(_spoolmanStatus.configured){
dot.style.color='var(--ok)';lbl.textContent=_spoolmanStatus.server||'verbunden';
} else {
dot.style.color='var(--txt2)';lbl.textContent='nicht konfiguriert';
}
}
function _buildSpoolmanSection(){
var sec=document.getElementById('fd-spoolman-section');
@@ -385,6 +396,11 @@ function applyLang(){
setText('setcat-lbl-display',T.settings_cat_display||'Darstellung');
setText('setcat-lbl-display2',T.settings_cat_display||'Darstellung');
setText('setcat-lbl-filament',T.settings_cat_filament||'Filament');
setText('setcat-lbl-integrations',T.settings_integrations||'Integrationen');
setText('modal-sec-spoolman',T.modal_sec_spoolman||'Spoolman');
setText('lbl-spoolman-url',T.lbl_spoolman_url||'Server-URL');
setText('lbl-spoolman-sync-rate',T.lbl_spoolman_sync_rate||'Sync-Rate (s, 0=aus)');
setText('modal-sec-obico',T.modal_sec_obico||'Obico');
setText('setcat-lbl-system',T.settings_version||'System');
setText('lbl-set-lang',T.settings_cat_language||'Sprache');
setText('lbl-set-theme',T.settings_cat_theme||'Hell / Dunkel umschalten');
@@ -1058,6 +1074,10 @@ function openSettings(){
pi.value=sec;
}
renderFilamentMapping(d.filament_profiles||{});
// Spoolman
var su=document.getElementById('s-spoolman-url');if(su)su.value=d.spoolman_server||'';
var sr=document.getElementById('s-spoolman-sync-rate');if(sr)sr.value=(d.spoolman_sync_rate!==undefined?d.spoolman_sync_rate:30);
_updateSpoolmanStatusDot();
});
// Sprach-Select im Settings-Panel mit aktueller Sprache spiegeln
var ls=document.getElementById('s-lang-select');
@@ -1626,6 +1646,8 @@ function saveSettings(){
print_start_dialog: parseInt((document.getElementById('s-file-ready-mode')||{}).value||'1',10),
web_upload_warning:webUploadWarning,
poll_interval: Math.min(60,Math.max(1,parseInt((document.getElementById('s-poll-interval')||{}).value,10)||3)),
spoolman_server: (document.getElementById('s-spoolman-url')||{}).value||'',
spoolman_sync_rate: Math.max(0,parseInt((document.getElementById('s-spoolman-sync-rate')||{}).value||'30',10)),
}).then(function(){
btn.textContent=T.update_restarting;
setTimeout(function(){

View File

@@ -435,6 +435,7 @@
<button class="set-cat" id="setcat-printer" onclick="showSettingsCat('printer')"><span>🖨</span> <span id="setcat-lbl-printer">Drucker</span></button>
<button class="set-cat" id="setcat-display" onclick="showSettingsCat('display')"><span>🎨</span> <span id="setcat-lbl-display">Darstellung</span></button>
<button class="set-cat" id="setcat-filament" onclick="showSettingsCat('filament')"><span>🧵</span> <span id="setcat-lbl-filament">Filament</span></button>
<button class="set-cat" id="setcat-integrations" onclick="showSettingsCat('integrations')"><span></span> <span id="setcat-lbl-integrations">Integrationen</span></button>
<button class="set-cat" id="setcat-system" onclick="showSettingsCat('system')"><span></span> <span id="setcat-lbl-system">System</span></button>
</div>
@@ -570,6 +571,33 @@
</div>
</div>
<!-- Integrationen -->
<div class="set-group" id="setgrp-integrations">
<!-- Spoolman -->
<div class="card">
<div class="card-title"><span>🧵</span> <span id="modal-sec-spoolman">Spoolman</span></div>
<div class="set-row">
<label id="lbl-spoolman-url">Server-URL</label>
<input type="text" id="s-spoolman-url" placeholder="http://spoolman:7912" style="width:200px">
</div>
<div class="set-row">
<label id="lbl-spoolman-sync-rate">Sync-Rate (s, 0=aus)</label>
<input type="number" id="s-spoolman-sync-rate" min="0" max="3600" value="30" style="width:80px">
</div>
<div id="spoolman-status-row" style="margin-top:6px;font-size:12px;color:var(--txt2)">
<span id="spoolman-status-dot"></span> <span id="spoolman-status-lbl"></span>
</div>
</div>
<!-- Obico -->
<div class="card" style="margin-top:10px">
<div class="card-title"><span>🕵</span> <span id="modal-sec-obico">Obico</span></div>
<div style="font-size:12px;color:var(--txt2);line-height:1.6" id="obico-info-box">
Obico wird über den <code>moonraker-obico</code>-Container konfiguriert.<br>
Config-Datei: <code id="obico-cfg-path">/mnt/dockerdata/KobraXStack/moonraker-obico/moonraker-obico.cfg</code>
</div>
</div>
</div>
<!-- System -->
<div class="set-group" id="setgrp-system">
<div class="card">

View File

@@ -123,6 +123,8 @@
"lbl_light": "💡 Licht",
"lbl_remaining": "Restzeit:",
"lbl_slicer_time": "Slicer-Schätzung:",
"lbl_spoolman_sync_rate": "Sync-Rate (s, 0=aus)",
"lbl_spoolman_url": "Server-URL",
"lbl_unload": "Ausziehen",
"lbl_zpos": "Z (mm)",
"log_auto": "⬇ Auto",
@@ -156,6 +158,8 @@
"log_topic_label": "Thema:",
"log_topic_print": "Druck",
"log_topic_status": "Status",
"modal_sec_obico": "Obico",
"modal_sec_spoolman": "Spoolman",
"nav_ams": "AMS",
"nav_browser": "Browser",
"nav_console": "Konsole",
@@ -231,6 +235,7 @@
"settings_file_ready_banner": "Druckleiste",
"settings_file_ready_dialog": "Druckdialog",
"settings_file_ready_mode": "Nach Upload: Druckstart-Verhalten",
"settings_integrations": "Integrationen",
"settings_language": "Sprache",
"settings_mode_id": "Mode-ID",
"settings_mode_id_placeholder": "20030",

View File

@@ -123,6 +123,8 @@
"lbl_light": "💡 Light",
"lbl_remaining": "Remaining:",
"lbl_slicer_time": "Slicer estimate:",
"lbl_spoolman_sync_rate": "Sync rate (s, 0=off)",
"lbl_spoolman_url": "Server URL",
"lbl_unload": "Unload",
"lbl_zpos": "Z (mm)",
"log_auto": "⬇ Auto",
@@ -156,6 +158,8 @@
"log_topic_label": "Topic:",
"log_topic_print": "Print",
"log_topic_status": "Status",
"modal_sec_obico": "Obico",
"modal_sec_spoolman": "Spoolman",
"nav_ams": "AMS",
"nav_browser": "Browser",
"nav_console": "Console",
@@ -231,6 +235,7 @@
"settings_file_ready_banner": "Print bar",
"settings_file_ready_dialog": "Print dialog",
"settings_file_ready_mode": "After upload: Start print behavior",
"settings_integrations": "Integrations",
"settings_language": "Language",
"settings_mode_id": "Mode ID",
"settings_mode_id_placeholder": "20030",

View File

@@ -123,6 +123,8 @@
"lbl_light": "💡 Luz",
"lbl_remaining": "Restante:",
"lbl_slicer_time": "Estimación del slicer:",
"lbl_spoolman_sync_rate": "Tasa de sincronización (s, 0=desact.)",
"lbl_spoolman_url": "URL del servidor",
"lbl_unload": "Descargar",
"lbl_zpos": "Z (mm)",
"log_auto": "⬇ Auto",
@@ -156,6 +158,8 @@
"log_topic_label": "Tema:",
"log_topic_print": "Impresión",
"log_topic_status": "Estado",
"modal_sec_obico": "Obico",
"modal_sec_spoolman": "Spoolman",
"nav_ams": "AMS",
"nav_browser": "Explorador",
"nav_console": "Consola",
@@ -231,6 +235,7 @@
"settings_file_ready_banner": "Barra de impresión",
"settings_file_ready_dialog": "Diálogo de impresión",
"settings_file_ready_mode": "Después de carga: Comportamiento de inicio de impresión",
"settings_integrations": "Integraciones",
"settings_language": "Idioma",
"settings_mode_id": "ID de modo",
"settings_mode_id_placeholder": "20030",

View File

@@ -123,6 +123,8 @@
"lbl_light": "💡 Lumière",
"lbl_remaining": "Restant :",
"lbl_slicer_time": "Estimation slicer :",
"lbl_spoolman_sync_rate": "Taux de sync. (s, 0=désact.)",
"lbl_spoolman_url": "URL du serveur",
"lbl_unload": "Décharger",
"lbl_zpos": "Z (mm)",
"log_auto": "⬇ Auto",
@@ -156,6 +158,8 @@
"log_topic_label": "Sujet :",
"log_topic_print": "Impression",
"log_topic_status": "Statut",
"modal_sec_obico": "Obico",
"modal_sec_spoolman": "Spoolman",
"nav_ams": "AMS",
"nav_browser": "Navigateur",
"nav_console": "Console",
@@ -231,6 +235,7 @@
"settings_file_ready_banner": "Barre d'impression",
"settings_file_ready_dialog": "Dialogue d'impression",
"settings_file_ready_mode": "Après téléchargement : Comportement de démarrage d'impression",
"settings_integrations": "Intégrations",
"settings_language": "Langue",
"settings_mode_id": "ID du mode",
"settings_mode_id_placeholder": "20030",

View File

@@ -123,6 +123,8 @@
"lbl_light": "💡 Luce",
"lbl_remaining": "Rimanente:",
"lbl_slicer_time": "Stima slicer:",
"lbl_spoolman_sync_rate": "Frequenza sync (s, 0=disatt.)",
"lbl_spoolman_url": "URL server",
"lbl_unload": "Rimuovi",
"lbl_zpos": "Z (mm)",
"log_auto": "⬇ Auto",
@@ -156,6 +158,8 @@
"log_topic_label": "Argomento:",
"log_topic_print": "Stampa",
"log_topic_status": "Stato",
"modal_sec_obico": "Obico",
"modal_sec_spoolman": "Spoolman",
"nav_ams": "AMS",
"nav_browser": "Browser",
"nav_console": "Console",
@@ -231,6 +235,7 @@
"settings_file_ready_banner": "Barra di stampa",
"settings_file_ready_dialog": "Finestra di dialogo stampa",
"settings_file_ready_mode": "Dopo il caricamento: Comportamento di avvio stampa",
"settings_integrations": "Integrazioni",
"settings_language": "Lingua",
"settings_mode_id": "ID modalità",
"settings_mode_id_placeholder": "20030",

View File

@@ -123,6 +123,8 @@
"lbl_light": "💡 灯光",
"lbl_remaining": "剩余时间:",
"lbl_slicer_time": "切片预估:",
"lbl_spoolman_sync_rate": "同步频率0=关闭)",
"lbl_spoolman_url": "服务器地址",
"lbl_unload": "退料",
"lbl_zpos": "Z (mm)",
"log_auto": "⬇ 自动",
@@ -156,6 +158,8 @@
"log_topic_label": "主题:",
"log_topic_print": "打印",
"log_topic_status": "状态",
"modal_sec_obico": "Obico",
"modal_sec_spoolman": "Spoolman",
"nav_ams": "AMS",
"nav_browser": "浏览器",
"nav_console": "控制台",
@@ -231,6 +235,7 @@
"settings_file_ready_banner": "打印栏",
"settings_file_ready_dialog": "打印对话框",
"settings_file_ready_mode": "上传后:开始打印行为",
"settings_integrations": "集成",
"settings_language": "语言",
"settings_mode_id": "模式 ID",
"settings_mode_id_placeholder": "20030",