forked from viewit/KX-Bridge-Release
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| be110fd766 | |||
| 77fce988d7 | |||
| fe1815c76f | |||
| 29a4262a2a | |||
| e753bcdb03 | |||
| 73ecb1e618 | |||
| 08d9a7ab0f | |||
| 93f72f0be9 | |||
| 812936d90d | |||
| 326453e2fd | |||
| ffd8ed09d5 | |||
| dc7e92688b | |||
| 3e1ba9df4b | |||
| 41f4700b24 | |||
| 216b2de2c0 | |||
| 394b0e69ab | |||
| 877cddb1ba | |||
| c9043e9630 | |||
| 6165a7f62a | |||
| fa8e0c1491 | |||
| 282c02ae0a | |||
| 72f77d92af |
31
.claude/agents/changelog.md
Normal file
31
.claude/agents/changelog.md
Normal 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.
|
||||
32
.claude/agents/docker-check.md
Normal file
32
.claude/agents/docker-check.md
Normal 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.
|
||||
23
.claude/agents/moonraker-debug.md
Normal file
23
.claude/agents/moonraker-debug.md
Normal 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 0–3
|
||||
- `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)
|
||||
25
.claude/agents/nightly-prep.md
Normal file
25
.claude/agents/nightly-prep.md
Normal 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]
|
||||
24
.claude/agents/reviewer.md
Normal file
24
.claude/agents/reviewer.md
Normal 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 lane1–lane4, kein Slot-Mapping 0–3
|
||||
- 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
|
||||
26
.claude/agents/test-writer.md
Normal file
26
.claude/agents/test-writer.md
Normal 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 lane1–lane4
|
||||
- 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
12
.claude/settings.json
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
29
.gitea/ISSUE_TEMPLATE/bug_report.md
Normal file
29
.gitea/ISSUE_TEMPLATE/bug_report.md
Normal 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 -->
|
||||
```
|
||||
14
.gitea/ISSUE_TEMPLATE/feature_request.md
Normal file
14
.gitea/ISSUE_TEMPLATE/feature_request.md
Normal 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? -->
|
||||
21
.gitea/pull_request_template.md
Normal file
21
.gitea/pull_request_template.md
Normal 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
|
||||
88
.gitea/workflows/nightly.yml
Normal file
88
.gitea/workflows/nightly.yml
Normal 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
|
||||
34
.gitea/workflows/pr-check.yml
Normal file
34
.gitea/workflows/pr-check.yml
Normal 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/') != '' }}
|
||||
87
.gitea/workflows/release.yml
Normal file
87
.gitea/workflows/release.yml
Normal 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
7
.gitignore
vendored
@@ -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
102
CONTRIBUTING.md
Normal 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.
|
||||
14
README.es.md
14
README.es.md
@@ -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>
|
||||
|
||||
[](https://ko-fi.com/viewitde)
|
||||
@@ -130,11 +139,6 @@ Impresora → Tipo de conexión **Moonraker** → Host: `http://IP-DEL-PUENTE:71
|
||||
|
||||
---
|
||||
|
||||
## 📺 Vídeo tutorial
|
||||
|
||||
[](https://www.youtube.com/watch?v=1Ql4wfH27fM)
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Slicer recomendado
|
||||
|
||||
|
||||
31
agents.md
Normal file
31
agents.md
Normal 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 lane1–lane4
|
||||
- Registry: `gitea.it-drui.de/viewit/kx-bridge`
|
||||
- Default PR target: `nightly`
|
||||
@@ -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
210
docker-compose-KX.yml
Normal 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
|
||||
@@ -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():
|
||||
|
||||
18
moonraker-obico.cfg.example
Normal file
18
moonraker-obico.cfg.example
Normal 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
31
package-lock.json
generated
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
package.json
23
package.json
@@ -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
21
pull_request_template.md
Normal 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
|
||||
@@ -1,2 +0,0 @@
|
||||
# Code formatting
|
||||
black>=24.1.0
|
||||
@@ -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(){
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user