Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f192a9943d | |||
| eb7fd44f68 | |||
| e5b2a19192 | |||
| 2f59a2b02b | |||
| bc9bfb58ea | |||
| ac309d5d3d | |||
| 38d98666c4 | |||
| e7c978a067 | |||
| d7c2dccef5 | |||
| e59550b5a0 | |||
| 5871e851da | |||
| e70e9c82d7 | |||
| be110fd766 | |||
| 77fce988d7 | |||
| fe1815c76f | |||
| 29a4262a2a | |||
| e753bcdb03 | |||
| 73ecb1e618 | |||
| 08d9a7ab0f | |||
| 93f72f0be9 | |||
| 812936d90d | |||
| 326453e2fd | |||
| ffd8ed09d5 | |||
| dc7e92688b | |||
| 3e1ba9df4b | |||
| 41f4700b24 | |||
| 216b2de2c0 | |||
| 394b0e69ab | |||
| 877cddb1ba | |||
| c9043e9630 | |||
| 6165a7f62a | |||
| fa8e0c1491 | |||
| 282c02ae0a | |||
| 72f77d92af | |||
| 3595cf839c | |||
| 2b39cc1a78 | |||
| d20308cf2c | |||
| 710c4831c2 | |||
| 5bff7adad0 | |||
| eea570052f | |||
| 303297bfbf | |||
| 6b9ad9d426 | |||
| ed30568092 | |||
| 1f300589d1 | |||
| 930e3774af | |||
| 636889bdbc | |||
| 3f6ea269e6 | |||
| 3fff6e25f0 |
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
|
||||
}
|
||||
}
|
||||
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
|
||||
140
.gitea/workflows/nightly.yml
Normal file
140
.gitea/workflows/nightly.yml
Normal file
@@ -0,0 +1,140 @@
|
||||
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: |
|
||||
VERSION=$(cat VERSION)
|
||||
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-${VERSION}" \
|
||||
.
|
||||
|
||||
- name: Create Gitea Nightly Release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
run: |
|
||||
VERSION=$(cat VERSION)
|
||||
TAG="nightly-${VERSION}"
|
||||
git config user.name "gitea-actions"
|
||||
git config user.email "actions@it-drui.de"
|
||||
|
||||
# Letzten nightly-Tag finden (für Changelog-Range)
|
||||
git fetch --tags origin 2>/dev/null || true
|
||||
PREV_TAG=$(git tag | grep '^nightly-' | sort | tail -1)
|
||||
|
||||
# Commits seit letztem Tag sammeln
|
||||
if [ -n "$PREV_TAG" ]; then
|
||||
COMMITS=$(git log "${PREV_TAG}..HEAD" --pretty=format:"- %s" --no-merges 2>/dev/null || true)
|
||||
else
|
||||
COMMITS=$(git log --pretty=format:"- %s" --no-merges -20 2>/dev/null || true)
|
||||
fi
|
||||
[ -z "$COMMITS" ] && COMMITS="- Automatischer Nightly-Build"
|
||||
|
||||
# Body in Temp-Datei (vermeidet YAML-Probleme mit Sonderzeichen wie > oder ```)
|
||||
BODY_FILE=$(mktemp)
|
||||
printf '## KX-Bridge %s -- Nightly Build\n\n' "$VERSION" > "$BODY_FILE"
|
||||
printf '[experimentell] Ungetestete Features, nur fuer Tester geeignet.\n\n' >> "$BODY_FILE"
|
||||
printf '### Aenderungen seit `%s`\n\n' "${PREV_TAG:-erstem Commit}" >> "$BODY_FILE"
|
||||
printf '%s\n\n---\n\n' "$COMMITS" >> "$BODY_FILE"
|
||||
printf '### Docker-Image aktualisieren\n\n```bash\ndocker compose pull && docker compose up -d\n```\n\n' >> "$BODY_FILE"
|
||||
printf 'Image-Tag: `gitea.it-drui.de/viewit/kx-bridge:nightly`\n' >> "$BODY_FILE"
|
||||
|
||||
# Tag setzen
|
||||
git tag -f "$TAG"
|
||||
git push https://gitea-actions:${GITEA_TOKEN}@gitea.it-drui.de/viewit/KX-Bridge-Release.git "$TAG" --force
|
||||
|
||||
# curl installieren (BusyBox wget kann kein DELETE/POST mit Headers)
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
if ! apk add --no-cache curl 2>/dev/null; then
|
||||
wget -qO /usr/local/bin/curl \
|
||||
"https://github.com/moparisthebest/static-curl/releases/download/v8.6.0/curl-amd64"
|
||||
chmod +x /usr/local/bin/curl
|
||||
fi
|
||||
fi
|
||||
|
||||
# Altes Release loeschen falls vorhanden
|
||||
curl -s -X DELETE \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases/tags/${TAG}" \
|
||||
2>/dev/null || true
|
||||
|
||||
# Release erstellen (JSON-Body via awk escapen)
|
||||
BODY_JSON=$(awk '{
|
||||
gsub(/\\/, "\\\\"); gsub(/"/, "\\\""); gsub(/\t/, "\\t");
|
||||
printf "%s\\n", $0
|
||||
}' "$BODY_FILE" | awk 'BEGIN{printf "\""} {printf "%s", $0} END{printf "\""}')
|
||||
JSON_PAYLOAD="{\"tag_name\":\"${TAG}\",\"name\":\"KX-Bridge ${VERSION} Nightly\",\"body\":${BODY_JSON},\"draft\":false,\"prerelease\":true}"
|
||||
printf '%s' "$JSON_PAYLOAD" > /tmp/release_body.json
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary @/tmp/release_body.json \
|
||||
"https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases"
|
||||
rm -f "$BODY_FILE" /tmp/release_body.json
|
||||
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
|
||||
}"
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,3 +17,4 @@ config/*.ini
|
||||
data/
|
||||
|
||||
!data/orca_filaments.json
|
||||
.runner-token
|
||||
|
||||
133
CHANGELOG.de.md
133
CHANGELOG.de.md
@@ -1,5 +1,138 @@
|
||||
# Changelog
|
||||
|
||||
## [0.9.26] – 2026-06-21
|
||||
|
||||
### Neu
|
||||
- **Italienische Sprachunterstützung** (PR #66, @Alex_M). Die Bridge-UI ist jetzt vollständig auf Italienisch verfügbar.
|
||||
|
||||
### Behoben
|
||||
- **Kamera startete immer beim Druckbeginn** (Issue #50). `camera_on_print` fehlte in der `/api/state`-Antwort — JavaScript las `undefined` und startete die Kamera unabhängig vom Setting. Jetzt korrekt im State enthalten.
|
||||
- **Auto-Leveling-Setting wurde im Moonraker-Druckpfad ignoriert** (Issue #57). `handle_print_start` las den Wert nur aus den Bridge-Args, nicht aus dem Request-Body — Dialog-Checkbox und Per-Print-Override hatten keine Wirkung. Verhält sich jetzt identisch zum direkten Druckpfad.
|
||||
- **Filament-Mapping: Freitext-Felder durch Dropdowns ersetzt** (Issue #57). Falsch getippte Vendor/Name-Kombination brach das Profil-Matching ohne Fehlermeldung; Felder sind jetzt Dropdowns (Vendor → Profil, vendor-gefiltert), sodass nur gültige Kombinationen gespeichert werden können.
|
||||
- **Dashboard zeigte generischen Materialtyp statt Profilname** (Issue #57). AMS-Slot-Karten zeigen jetzt den gemappten Profilnamen (z.B. „eSUN PLA-Basic") statt nur „PLA". Fallback auf generischen Typ wenn kein Profil gemappt ist.
|
||||
- **Ghost-Profil auf leerem Slot** (Issue #57). Verwaiste Mappings für leere Slots wurden weiterhin angezeigt; leere Slots zeigen jetzt korrekt „–".
|
||||
- **Skip-Objects-Panel fehlte im Orca-Upload-Flow** (Issue #57). Panel erscheint jetzt in allen Druckflows; bei frischem Upload fragt die Bridge `fileDetails` beim Drucker nach und pollt die Objektliste bis zu 6 Sekunden nach.
|
||||
- **Banner und Dialog erschienen gleichzeitig** (Issue #57). Settings-Save setzt jetzt den Dialog-Cancel-State zurück, sodass der Slot-Mapper nach Wechsel des Start-Print-Verhaltens zuverlässig öffnet.
|
||||
- **„Leeren" lud idle-Datei beim nächsten Poll nach** (Issue #57). Leeren setzt jetzt den lokalen State sofort zurück (`file_ready`, `filename`, `thumbnail`) und löscht alle Dialog-Sperren — Vorschaubild und Aktions-Buttons verschwinden sofort und kommen nicht zurück.
|
||||
- **Material-Matching für „PLA Silk", „Matte PLA" etc.** (PR #64, @p2l). Modifier+Basis-Muster in beliebiger Wortreihenfolge werden jetzt auf den Basis-Typ normalisiert; Dash-Varianten (PLA-CF) bleiben weiterhin korrekt inkompatibel mit ihrem Basis-Typ.
|
||||
|
||||
## [0.9.25] – 2026-06-17
|
||||
|
||||
### Behoben
|
||||
- **Zufällige Abstürze / Container-Restarts — Segfault in `libcrypto.so.3`
|
||||
(Issue #53).** Der MQTT-über-TLS-Client teilte einen einzelnen SSL-Socket
|
||||
zwischen dem Reader-Thread (`recv`) und den Sender-Threads (`sendall`), ohne sie
|
||||
zu serialisieren. CPythons `ssl`-Modul erlaubt kein gleichzeitiges Lesen und
|
||||
Schreiben auf demselben Socket — die Überlappung korrumpierte den internen
|
||||
OpenSSL-Zustand und löste eine Heap-Corruption + Segfault aus, die auf manchen
|
||||
Hosts timing-bedingt zuverlässig auftrat. Sämtliche Socket-Zugriffe (recv /
|
||||
sendall / close / reconnect) werden nun unter einem einzigen Lock serialisiert;
|
||||
der Reader prüft die Bereitschaft mit `select()` außerhalb des Locks, damit die
|
||||
Sender nie ausgehungert werden. Reconnect und Disconnect tauschen den Socket
|
||||
jetzt atomar. Dank an @BasK für den detaillierten Fault-Handler-Trace.
|
||||
- **File-Browser akzeptierte Nicht-GCode-Uploads (Issue #59).** Drag & Drop umging
|
||||
den `accept`-Filter des Dateidialogs, sodass z.B. ein JPG hochgeladen werden
|
||||
konnte. Uploads werden jetzt client- und serverseitig validiert; nur `.gcode`,
|
||||
`.gcode.3mf`, `.3mf` und `.bgcode` werden akzeptiert. Dank an @gangoke.
|
||||
|
||||
## [0.9.24] – 2026-06-16
|
||||
|
||||
### Neu
|
||||
- **Objekte überspringen in jedem Druck-Flow (Issue #57).** Der „Objekte
|
||||
überspringen"-Bereich im Slot-Mapper erschien bisher nur beim Druck aus dem
|
||||
Browser-Tab. Er ist jetzt in allen Flows verfügbar (inkl. Upload / Print-Leiste),
|
||||
standardmäßig eingeklappt hinter einem `✂ Objekte überspringen (N)`-Header, damit
|
||||
der Dialog kompakt bleibt — Klick klappt Vorschau + Checkliste auf.
|
||||
- **Slot-Mapper zeigt konkreten Profilnamen (Issue #57).** Jeder Slot zeigt nun das
|
||||
zugeordnete Filament-Profil (z.B. „PolyTerra PLA — Polymaker") in den Dropdown-
|
||||
Optionen und als Hover-Tooltip am Slot-Marker, statt nur des generischen Typs.
|
||||
Fällt auf den generischen Typ zurück, wenn kein Profil gemappt ist.
|
||||
|
||||
## [0.9.23] – 2026-06-16
|
||||
|
||||
### Neu
|
||||
- **Druckdialog nach Upload automatisch öffnen.** Eine neue Einstellung
|
||||
`print_start_dialog` (Einstellungen → Drucker → „Druckstart-Verhalten") steuert,
|
||||
was nach einem Upload bei leerlaufendem Drucker passiert: „Print-Dialog" öffnet
|
||||
den Slot-Zuordnungs-Dialog automatisch, „Print-Leiste" behält das bisherige
|
||||
Banner. Basiert auf PR #56 von @gangoke.
|
||||
- **Auto-Leveling-Schalter pro Druck.** Der Druckdialog hat jetzt eine eigene
|
||||
Auto-Leveling-Checkbox, die den globalen Standard für einen einzelnen Druck
|
||||
überschreibt.
|
||||
|
||||
### Behoben
|
||||
- **Objekt-Skip wurde beim Druckstart still ignoriert (PR #56, @gangoke).** Der
|
||||
Skip-Befehl wurde gesendet, *bevor* der Drucker im `printing`-Status war, und
|
||||
daher verworfen. Der Skip wird nun in einer Retry-Schleife erneut angewendet,
|
||||
sobald der Druck bestätigt läuft — mit einer Pending-Sperre, damit die UI den
|
||||
Skip-Status nicht vorzeitig zurücksetzt.
|
||||
- **Upload während eines laufenden Drucks überschrieb die Vorschau des laufenden
|
||||
Auftrags.** Ein neuer Upload während des Drucks ersetzt nicht mehr Thumbnail /
|
||||
file_ready des Auftrags auf dem Druckbett.
|
||||
|
||||
## [0.9.22] – 2026-06-16
|
||||
|
||||
### Neu
|
||||
- **Neu strukturiertes Einstellungs-Panel.** Das Einstellungs-Modal wurde durch
|
||||
ein dauerhaftes Master-Detail-Panel mit fünf Kategorien ersetzt: Verbindung,
|
||||
Drucker, Darstellung, Filament und System. Das Poll-Intervall ist nun live
|
||||
einstellbar.
|
||||
- **Vendor-Sichtbarkeitsfilter (Issue #41).** Eine neue Checkliste in den
|
||||
Filament-Einstellungen beschränkt das Slot-Profil-Dropdown auf bestimmte
|
||||
Hersteller. „Generic" und eigene importierte Profile sind immer sichtbar.
|
||||
- **Idle-Datei-Aktionen in der Fortschritts-Karte (Issue #55).** Nach einem
|
||||
Upload bei leerlaufendem Drucker erscheinen drei Schnellaktionen direkt in der
|
||||
Fortschritts-Karte: ▶ Drucken, ⚙ Slots zuordnen und ✕ Leeren.
|
||||
|
||||
### Behoben
|
||||
- **Mobileraker-Kompatibilität (Issue #48).** Absturz in `ConfigExtruder.fromJson`
|
||||
(leeres `configfile.config`), Hänger beim Refresh (Metadata-Endlosschleife) und
|
||||
fehlende ETA/Restzeit behoben.
|
||||
|
||||
## [0.9.21] – 2026-06-14
|
||||
|
||||
### Behoben
|
||||
- **Kamera-Stream auf Android (Chrome / Firefox) nicht sichtbar.** Android-Browser
|
||||
unterstützen `multipart/x-mixed-replace` (MJPEG) nicht. Die UI erkennt Android
|
||||
jetzt automatisch und fällt auf Snapshot-Polling mit 5 fps zurück
|
||||
(`/api/camera/snapshot` alle 200 ms) — keine Server-Änderung nötig.
|
||||
|
||||
### Geändert
|
||||
- Docker-Image auf **Debian 12 (Bookworm)** gepinnt (`python:3.11-slim-bookworm`),
|
||||
um Kompatibilitätsprobleme mit glibc 2.41 zu vermeiden, die das aktuell von
|
||||
`python:3.11-slim` gezogene Debian 13 Basis-Image mitbringt.
|
||||
- MQTT- und HTTP-Verbindungen erzwingen jetzt **IPv4** (`AF_INET`), um
|
||||
Verbindungsfehler auf Hosts zu verhindern, bei denen der Drucker nur über IPv4
|
||||
erreichbar ist, das OS aber IPv6 bevorzugt.
|
||||
- Extruder-Stub in der Moonraker-`configfile`-Antwort enthält jetzt `sensor_type`
|
||||
und `filament_diameter` — behebt einen Mobileraker-Absturz
|
||||
(`Null is not a subtype of Object`, Issue #48).
|
||||
|
||||
## [0.9.20] – 2026-06-08
|
||||
|
||||
### Neu
|
||||
- **Französische Sprachunterstützung (PR #45 von @Nathacks)**
|
||||
- **Z-Höhe in der Print-UI (PR #49 von @Nathacks).** Zeigt die aktuelle
|
||||
Z-Position in mm unterhalb des Layer-Zählers.
|
||||
|
||||
### Behoben
|
||||
- **Kamera-Autostart ignorierte das "Kamera bei Druckstart einschalten"-
|
||||
Setting nach einem Bridge-Restart (Issue #50).** Das Setting wurde in
|
||||
der Prozessumgebung gecacht — nach dem Speichern in der UI überlebte
|
||||
der alte Wert den Restart und der neue Wert aus `config.ini` wurde
|
||||
nicht gelesen.
|
||||
- **Kamera startete nach manuellem Stopp während eines Drucks automatisch
|
||||
neu (Issue #50).** Ein neues `_camera_user_stopped`-Flag unterdrückt
|
||||
den Autostart für die aktuelle Drucksitzung. Es wird beim Druckende
|
||||
zurückgesetzt.
|
||||
- **Falscher "Stream nicht verfügbar"-Fehler-Toast beim manuellen
|
||||
Kamera-Stopp.** Der Bild-Fehler-Handler war noch registriert als
|
||||
`img.src` geleert wurde.
|
||||
- **JS-Fehler (`ReferenceError: br is not defined`) beim Licht-Toggle.**
|
||||
Variable wurde aus dem falschen Scope referenziert.
|
||||
- Webcam-URLs sind jetzt absolut, damit Mobileraker/Obico-Clients sie
|
||||
erreichen können.
|
||||
|
||||
## [0.9.19.1] – 2026-06-04
|
||||
|
||||
### Behoben
|
||||
|
||||
199
CHANGELOG.md
199
CHANGELOG.md
@@ -1,24 +1,195 @@
|
||||
# Changelog
|
||||
|
||||
## [0.9.26] – 2026-06-21
|
||||
|
||||
### New
|
||||
- **Italian language support** (PR #66, @Alex_M). The bridge UI is now fully
|
||||
available in Italian.
|
||||
|
||||
### Fixed
|
||||
- **Camera always started at print begin** (issue #50). `camera_on_print` was
|
||||
missing from the `/api/state` response — JavaScript read `undefined` and started
|
||||
the camera regardless of the setting. Now correctly exposed in state.
|
||||
- **Auto-leveling setting ignored in Moonraker print path** (issue #57).
|
||||
`handle_print_start` read the value only from bridge args, not from the request
|
||||
body, so the dialog checkbox and the per-print override had no effect. Now
|
||||
behaves identically to the direct print path.
|
||||
- **Filament mapping free-text fields replaced by dropdowns** (issue #57). A
|
||||
mistyped vendor/name broke profile matching silently; fields are now dropdowns
|
||||
(vendor → profile, vendor-filtered) so only valid combinations can be saved.
|
||||
- **Dashboard showed generic material type instead of profile name** (issue #57).
|
||||
AMS slot cards now display the mapped profile name (e.g. "eSUN PLA-Basic")
|
||||
instead of just "PLA". Falls back to the generic type when no profile is mapped.
|
||||
- **Ghost profile shown on empty slot** (issue #57). Stale mappings for empty
|
||||
slots were still rendered; empty slots now correctly show "–".
|
||||
- **Skip-Objects panel missing in Orca upload flow** (issue #57). Panel now
|
||||
appears in all print flows; on fresh upload the bridge requests `fileDetails`
|
||||
from the printer and retries the object list for up to 6 s.
|
||||
- **Banner and dialog appeared simultaneously** (issue #57). Settings save now
|
||||
resets the dialog cancel state so the slot mapper reliably opens after toggling
|
||||
Start Print Behavior.
|
||||
- **"Clear" reloaded idle file on next poll** (issue #57). Clear now immediately
|
||||
resets local state (`file_ready`, `filename`, `thumbnail`) and clears all dialog
|
||||
locks — the preview and action buttons disappear instantly and do not return.
|
||||
- **Material matching for "PLA Silk", "Matte PLA" etc.** (PR #64, @p2l).
|
||||
Modifier+base patterns in any word order are now normalised to the base type;
|
||||
dash-suffix variants (PLA-CF) remain correctly incompatible with their base.
|
||||
|
||||
## [0.9.25] – 2026-06-17
|
||||
|
||||
### Fixed
|
||||
- **Random crashes / container restarts — segfault in `libcrypto.so.3` (issue #53).**
|
||||
The MQTT-over-TLS client shared a single SSL socket between the reader thread
|
||||
(`recv`) and the sender threads (`sendall`) without serializing them. CPython's
|
||||
`ssl` module does not allow concurrent read and write on the same socket — the
|
||||
overlap corrupted OpenSSL's internal state, causing a heap corruption and a
|
||||
segfault that manifested reliably on some hosts (timing-dependent). All socket
|
||||
access (recv / sendall / close / reconnect) is now serialized under a single
|
||||
lock; the reader probes readiness with `select()` outside the lock so senders
|
||||
are never starved. Reconnect and disconnect now swap the socket atomically.
|
||||
Thanks to @BasK for the detailed fault-handler trace that pinpointed this.
|
||||
- **File browser accepted non-GCode uploads (issue #59).** Drag & drop bypassed
|
||||
the file picker's `accept` filter, so e.g. a JPG could be uploaded. Uploads are
|
||||
now validated both client- and server-side; only `.gcode`, `.gcode.3mf`, `.3mf`
|
||||
and `.bgcode` are accepted. Thanks @gangoke.
|
||||
|
||||
## [0.9.24] – 2026-06-16
|
||||
|
||||
### New
|
||||
- **Skip Objects available in every print flow (issue #57).** The "Skip objects"
|
||||
panel in the Slot Mapper used to appear only when printing from the Browser tab.
|
||||
It now shows in all flows (upload / print bar included), collapsed by default
|
||||
behind a `✂ Skip objects (N)` header to keep the dialog compact, expanding on
|
||||
click with the object preview and checklist.
|
||||
- **Slot Mapper shows the specific profile name (issue #57).** Each slot now
|
||||
displays its mapped filament profile (e.g. "PolyTerra PLA — Polymaker") in the
|
||||
dropdown options and as a hover tooltip on the slot marker, instead of just the
|
||||
generic type. Falls back to the generic type when no profile is mapped.
|
||||
|
||||
## [0.9.23] – 2026-06-16
|
||||
|
||||
### New
|
||||
- **Auto-open print dialog after upload.** A new `print_start_dialog` setting
|
||||
(Settings → Printer → "Start Print Behavior") controls what happens after a
|
||||
file is uploaded while the printer is idle: `Print Dialog` opens the
|
||||
slot-assignment dialog automatically, `Print Bar` keeps the previous banner
|
||||
behaviour. Based on PR #56 by @gangoke.
|
||||
- **Per-print auto-leveling toggle.** The print dialog now has its own
|
||||
auto-leveling checkbox that overrides the global default for a single print.
|
||||
|
||||
### Fixed
|
||||
- **Object skip was silently ignored at print start (PR #56, @gangoke).** The
|
||||
skip command was sent *before* the printer entered the `printing` state, so it
|
||||
was dropped. The skip is now re-applied in a retry loop once the print is
|
||||
confirmed running, with a pending-lock so the UI doesn't reset the skip state
|
||||
prematurely.
|
||||
- **Upload during an active print overwrote the running job's preview.**
|
||||
Uploading a new file while printing no longer replaces the thumbnail /
|
||||
file_ready of the job currently on the bed.
|
||||
|
||||
## [0.9.22] – 2026-06-16
|
||||
|
||||
### New
|
||||
- **Restructured Settings panel.** The settings modal has been replaced by a
|
||||
persistent Master-Detail panel with five categories: Connection, Printer,
|
||||
Appearance, Filament, and System. Poll interval is now adjustable live.
|
||||
- **Vendor visibility filter (issue #41).** A new checklist in the Filament
|
||||
settings lets you restrict the slot profile dropdown to specific manufacturers.
|
||||
"Generic" and your own imported profiles are always visible. The list updates
|
||||
automatically after a profile import.
|
||||
- **Idle file actions in the progress card (issue #55).** After uploading a file
|
||||
while the printer is idle, three quick-action buttons appear directly in the
|
||||
progress card: ▶ Print, ⚙ Map Slots, and ✕ Clear — matching the file browser
|
||||
workflow without navigating away.
|
||||
|
||||
### Fixed
|
||||
- **Mobileraker: app crashed with `Null is not a subtype of Object` in
|
||||
`ConfigExtruder.fromJson` (issue #48).** `configfile.config` was returned as
|
||||
an empty object `{}`. Mobileraker parses both `configfile.settings` and
|
||||
`configfile.config` through the same strict Dart parser — both are now
|
||||
populated with the same extruder/bed/stepper stub.
|
||||
- **Mobileraker: app hung indefinitely on refresh (issue #48).** The WebSocket
|
||||
`server.files.metadata` handler called a non-existent store method
|
||||
(`get_file_by_filename`), always returning empty metadata. Mobileraker retried
|
||||
this thousands of times per second. Both the HTTP and WS paths now share a
|
||||
single `_build_file_metadata()` method.
|
||||
- **Mobileraker: ETA / remaining time not shown (issue #48).** A side effect of
|
||||
the metadata loop fix — once `currentFile` resolves, Mobileraker can calculate
|
||||
ETA from `estimated_time`.
|
||||
- **Mobileraker: `notify_status_update` triggered repeated `ConfigFile.parse`
|
||||
(issue #48).** Static objects (`configfile`, `webhooks`, `heaters`, `history`)
|
||||
were included in every live status push. They are now filtered out; only live
|
||||
telemetry is broadcast.
|
||||
- `motion_report` (`live_position`, `live_velocity`) added to printer objects
|
||||
for Mobileraker motion display.
|
||||
- Saving filament slot profiles no longer silently drops the `visible_vendors`
|
||||
setting from `config.ini`.
|
||||
|
||||
## [0.9.21] – 2026-06-14
|
||||
|
||||
### Fixed
|
||||
- **Camera stream not visible on Android (Chrome / Firefox).** Android
|
||||
browsers do not support `multipart/x-mixed-replace` (MJPEG). The UI
|
||||
now detects Android and falls back to snapshot-polling at 5 fps
|
||||
(`/api/camera/snapshot` every 200 ms) — no server-side change needed.
|
||||
|
||||
### Changed
|
||||
- Docker image now pinned to **Debian 12 (Bookworm)** (`python:3.11-slim-bookworm`)
|
||||
to avoid glibc 2.41 compatibility issues introduced by the Debian 13
|
||||
base image that `python:3.11-slim` recently started pulling.
|
||||
- MQTT and HTTP connections now **force IPv4** (`AF_INET`) to prevent
|
||||
connection failures on hosts where the printer is only reachable via
|
||||
IPv4 but the OS prefers IPv6.
|
||||
- Extruder stub in the Moonraker `configfile` response now includes
|
||||
`sensor_type` and `filament_diameter` — fixes a Mobileraker crash
|
||||
(`Null is not a subtype of Object`, issue #48).
|
||||
|
||||
## [0.9.20] – 2026-06-08
|
||||
|
||||
### New
|
||||
- **French language support (PR #45 by @Nathacks)**
|
||||
- **Z height display in the print UI (PR #49 by @Nathacks).** Shows
|
||||
current Z position in mm below the layer counter.
|
||||
|
||||
### Fixed
|
||||
- **Camera auto-start ignored "Enable camera on print start" setting
|
||||
after a bridge restart (issue #50).** The setting was cached in the
|
||||
process environment — after saving it in the UI, the old value
|
||||
survived the restart and the new value from `config.ini` was never
|
||||
read.
|
||||
- **Camera restarted automatically after manual stop during a print
|
||||
(issue #50).** A new `_camera_user_stopped` flag suppresses
|
||||
auto-restart for the current print session. It resets when the
|
||||
print ends.
|
||||
- **Spurious "stream unavailable" error toast when stopping the camera
|
||||
manually.** The image error handler was still registered when
|
||||
`img.src` was cleared.
|
||||
- **JS error (`ReferenceError: br is not defined`) when toggling the
|
||||
light.** Variable was referenced from the wrong scope.
|
||||
- Webcam URLs are now absolute so that Mobileraker/Obico clients can
|
||||
reach them.
|
||||
|
||||
## [0.9.19.1] – 2026-06-04
|
||||
|
||||
### Fixed
|
||||
- Standalone-Binaries (Linux/Windows) zeigten `vunknown` als Version.
|
||||
Die `VERSION`-Datei ist jetzt ins PyInstaller-Onefile eingebettet.
|
||||
- Bei fehlenden TLS-Zertifikaten (`anycubic_slicer.crt`/`.key`) gab
|
||||
es nur den rohen Fehler `[Errno 2] No such file or directory`. Die
|
||||
Bridge meldet jetzt klar, wo die Dateien hingelegt werden müssen
|
||||
und dass `anycubic-certs.zip` aus dem Gitea-Release stammt.
|
||||
- Standalone binaries (Linux/Windows) reported `vunknown` as their
|
||||
version. The `VERSION` file is now embedded into the PyInstaller
|
||||
onefile bundle.
|
||||
- When the TLS certificates (`anycubic_slicer.crt`/`.key`) were
|
||||
missing, the bridge only logged the raw `[Errno 2] No such file
|
||||
or directory`. It now states clearly where the files need to be
|
||||
placed and that `anycubic-certs.zip` from the Gitea release is the
|
||||
source.
|
||||
|
||||
### Changed
|
||||
- Filament-Profil-Liste neu kuratiert: 209 statt 399 Einträge.
|
||||
Profile die nur für drucker-spezifische Vendor-Bundles existieren
|
||||
(z.B. Eryone Thinker X400, Artillery M1 Pro, WonderMaker ZR,
|
||||
Tiertime, Cubicon, CoLiDo, Afinia, Snapmaker) sind rausgeflogen
|
||||
— OrcaSlicer hätte sie im Standard-Kobra-X-Setup beim Sync
|
||||
ohnehin nicht gefunden, weil die jeweiligen Vendor-Bundles nur
|
||||
bei aktivem Drucker-Vendor geladen werden. Für solche Filamente
|
||||
bleibt der Custom-Profile-Import (Issue #41) der Weg.
|
||||
- Filament profile list re-curated: 209 entries instead of 399.
|
||||
Profiles that only exist inside printer-specific vendor bundles
|
||||
(e.g. Eryone Thinker X400, Artillery M1 Pro, WonderMaker ZR,
|
||||
Tiertime, Cubicon, CoLiDo, Afinia, Snapmaker) were dropped —
|
||||
OrcaSlicer wouldn't have found them in a default Kobra X setup
|
||||
anyway, because the matching vendor bundle is only loaded when
|
||||
the corresponding printer vendor is active. For those filaments
|
||||
the custom profile import (issue #41) remains the way.
|
||||
|
||||
## [0.9.19] – 2026-06-02
|
||||
|
||||
|
||||
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.
|
||||
@@ -1,7 +1,9 @@
|
||||
FROM python:3.11-slim
|
||||
FROM python:3.11-slim-bookworm
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends ffmpeg && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
|
||||
27
README.de.md
27
README.de.md
@@ -21,7 +21,7 @@ Feedback willkommen.</sub>
|
||||
|
||||
[](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)
|
||||
|
||||
[](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)
|
||||
[](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)
|
||||
|
||||
[](https://www.youtube.com/watch?v=1Ql4wfH27fM)
|
||||
|
||||
@@ -29,6 +29,11 @@ Feedback willkommen.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
> [!CAUTION]
|
||||
> **Laufende Wartungsarbeiten** — Wir strukturieren das Repository um (Branch-Modell, CI-Workflows, Beitragsprozess). Es kann zu Änderungen bei Branch-Namen, PR-Templates und der Art der Veröffentlichungen kommen. Wir entschuldigen uns für etwaige Unannehmlichkeiten. Handhabung, Workflow und langfristige Wartbarkeit werden dadurch deutlich verbessert.
|
||||
>
|
||||
> 👉 Möchtest du beitragen? Bitte zuerst [CONTRIBUTING.md](CONTRIBUTING.md) lesen.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Was kann KX-Bridge?
|
||||
@@ -45,7 +50,7 @@ Feedback willkommen.</sub>
|
||||
| 🧩 | **Multi-Printer** — mehrere Drucker in **einer** Bridge-Instanz, Umschalten per Dropdown |
|
||||
| ➕ | **Drucker hinzufügen per Klick** — nur die IP eingeben, Zugangsdaten werden automatisch importiert |
|
||||
| 🔁 | **Robuster MQTT-Reconnect** — Bridge überlebt nächtlichen Drucker-Reboot ohne manuellen Neustart |
|
||||
| 🌐 | **Mehrsprachiges UI** — DE / EN / ES / 中文, Browser-Sprache automatisch erkannt |
|
||||
| 🌐 | **Mehrsprachiges UI** — DE / EN / ES / FR / IT / 中文, Browser-Sprache automatisch erkannt |
|
||||
| 🔄 | **Self-Update** — neue Versionen direkt im Browser installieren |
|
||||
| 🧠 | **OrcaSlicer** — volles Moonraker-Protokoll (HTTP + WebSocket); für korrekten Vendor-Match pro Slot den [OrcaSlicer-KX-Build](#-empfohlener-slicer) nutzen |
|
||||
|
||||
@@ -184,6 +189,24 @@ docker compose up -d --build # lokal selber bauen (statt zu pullen)
|
||||
|
||||
---
|
||||
|
||||
## 🌙 Nightly-Builds
|
||||
|
||||
Nightly-Builds enthalten die neuesten unveröffentlichten Features und werden automatisch bei jedem Entwicklungs-Push gebaut.
|
||||
Sie können instabil sein — für Tests oder frühen Zugriff auf neue Funktionen geeignet.
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yml -f docker-compose.nightly.yml pull
|
||||
docker compose -f docker-compose.yml -f docker-compose.nightly.yml up -d
|
||||
```
|
||||
|
||||
Zurück zum stabilen Release:
|
||||
|
||||
```bash
|
||||
docker compose pull && docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🩹 Troubleshooting
|
||||
|
||||
<details>
|
||||
|
||||
48
README.es.md
48
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)
|
||||
@@ -66,17 +75,43 @@ docker compose up -d
|
||||
|
||||
**Binario Linux (sin Docker):**
|
||||
```bash
|
||||
chmod +x kx-bridge && ./kx-bridge
|
||||
chmod +x kx-bridge-linux-amd64 && ./kx-bridge-linux-amd64
|
||||
```
|
||||
|
||||
**EXE Windows (sin Docker):**
|
||||
```
|
||||
kx-bridge.exe
|
||||
```
|
||||
> `config\` y `data\` se crean junto al EXE — instalación portátil.
|
||||
|
||||
> Con los binarios de Linux y Windows, `config/` y `data/` (configuración, SQLite, almacén de GCode)
|
||||
> viven junto al programa. Copia toda la carpeta para mover la instalación.
|
||||
> ⚠️ **Certificados TLS necesarios para el binario standalone**
|
||||
>
|
||||
> El bridge habla con el MQTT de la impresora vía mTLS y necesita dos
|
||||
> ficheros de certificado **junto al binario**:
|
||||
>
|
||||
> - `anycubic_slicer.crt`
|
||||
> - `anycubic_slicer.key`
|
||||
>
|
||||
> Ambos vienen en **`anycubic-certs.zip`** en la misma página de release.
|
||||
> Descárgalo y extrae los dos ficheros en el mismo directorio que
|
||||
> `kx-bridge-linux-amd64` o `kx-bridge.exe`. Sin ellos verás
|
||||
> `Verbindung fehlgeschlagen: TLS-Zertifikate fehlen …` (0.9.19.1+) o
|
||||
> `[Errno 2] No such file or directory` (versiones anteriores).
|
||||
>
|
||||
> Estructura correcta:
|
||||
> ```
|
||||
> ~/kx-bridge/
|
||||
> ├── kx-bridge-linux-amd64 (o kx-bridge.exe)
|
||||
> ├── anycubic_slicer.crt ← de anycubic-certs.zip
|
||||
> ├── anycubic_slicer.key ← de anycubic-certs.zip
|
||||
> └── config/ (se crea en el primer arranque)
|
||||
> ```
|
||||
>
|
||||
> Los usuarios de Docker no necesitan hacer esto — los certificados
|
||||
> están incluidos en la imagen.
|
||||
|
||||
> Con los binarios de Linux y Windows, `config/` y `data/` (configuración,
|
||||
> SQLite, almacén de GCode) viven junto al programa. Copia toda la carpeta
|
||||
> para mover la instalación.
|
||||
|
||||
**Python directamente:**
|
||||
```bash
|
||||
@@ -104,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
|
||||
|
||||
|
||||
27
README.md
27
README.md
@@ -20,7 +20,7 @@ officially tested or supported. Feedback welcome.</sub>
|
||||
|
||||
[](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)
|
||||
|
||||
[](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)
|
||||
[](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)
|
||||
|
||||
[](https://www.youtube.com/watch?v=1Ql4wfH27fM)
|
||||
|
||||
@@ -28,6 +28,11 @@ officially tested or supported. Feedback welcome.</sub>
|
||||
|
||||
</div>
|
||||
|
||||
> [!CAUTION]
|
||||
> **Ongoing maintenance work** — We are restructuring the repository (branch model, CI workflows, contribution process). You may notice changes to branch names, PR templates, and how releases are published. We apologise for any inconvenience. Handling, workflow, and long-term maintainability will be significantly improved as a result.
|
||||
>
|
||||
> 👉 Want to contribute? Please read [CONTRIBUTING.md](CONTRIBUTING.md) first.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Features
|
||||
@@ -44,7 +49,7 @@ officially tested or supported. Feedback welcome.</sub>
|
||||
| 🧩 | **Multi-printer** — multiple printers in **one** bridge instance, switch via dropdown |
|
||||
| ➕ | **Add a printer with one click** — just enter the IP, credentials are imported automatically |
|
||||
| 🔁 | **Robust MQTT reconnect** — bridge survives overnight printer reboots without manual restart |
|
||||
| 🌐 | **Multi-language UI** — DE / EN / ES / 中文, auto-detect browser locale |
|
||||
| 🌐 | **Multi-language UI** — DE / EN / ES / FR / IT / 中文, auto-detect browser locale |
|
||||
| 🔄 | **Self-update** — install new versions directly in the browser |
|
||||
| 🧠 | **OrcaSlicer** — full Moonraker protocol (HTTP + WebSocket); pair with the [OrcaSlicer-KX build](#-recommended-slicer) for proper per-slot vendor matching |
|
||||
|
||||
@@ -182,6 +187,24 @@ docker compose up -d --build # rebuild locally (instead of pulling)
|
||||
|
||||
---
|
||||
|
||||
## 🌙 Nightly Builds
|
||||
|
||||
Nightly builds contain the latest unreleased features and are built automatically on every development push.
|
||||
They may be unstable — use them for testing or early access to new functionality.
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yml -f docker-compose.nightly.yml pull
|
||||
docker compose -f docker-compose.yml -f docker-compose.nightly.yml up -d
|
||||
```
|
||||
|
||||
To go back to the stable release:
|
||||
|
||||
```bash
|
||||
docker compose pull && docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🩹 Troubleshooting
|
||||
|
||||
<details>
|
||||
|
||||
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
|
||||
|
||||
@@ -13,6 +13,7 @@ _BASE = pathlib.Path(sys.executable).parent if getattr(sys, "frozen", False) els
|
||||
CONFIG_SECTION_CONNECTION = "connection"
|
||||
CONFIG_SECTION_PRINT = "print"
|
||||
CONFIG_SECTION_BRIDGE = "bridge"
|
||||
CONFIG_SECTION_SPOOLMAN = "spoolman"
|
||||
|
||||
|
||||
def _find_config_file() -> pathlib.Path | None:
|
||||
@@ -61,7 +62,10 @@ def _load_config_file(path: pathlib.Path):
|
||||
"AUTO_LEVELING": (CONFIG_SECTION_PRINT, "auto_leveling"),
|
||||
"CAMERA_ON_PRINT": (CONFIG_SECTION_PRINT, "camera_on_print"),
|
||||
"WEB_UPLOAD_WARNING": (CONFIG_SECTION_PRINT, "web_upload_warning"),
|
||||
"PRINT_START_DIALOG": (CONFIG_SECTION_PRINT, "print_start_dialog"),
|
||||
"BRIDGE_PRINTER_NAME": (CONFIG_SECTION_BRIDGE, "printer_name"),
|
||||
"SPOOLMAN_SERVER": (CONFIG_SECTION_SPOOLMAN, "server"),
|
||||
"SPOOLMAN_SYNC_RATE": (CONFIG_SECTION_SPOOLMAN, "sync_rate"),
|
||||
}
|
||||
for env_key, (section, option) in mapping.items():
|
||||
if env_key not in os.environ:
|
||||
@@ -73,6 +77,18 @@ def _load_config_file(path: pathlib.Path):
|
||||
pass
|
||||
|
||||
|
||||
# Backward compatibility: old key FILE_READY_DIALOG → PRINT_START_DIALOG
|
||||
if "PRINT_START_DIALOG" not in os.environ:
|
||||
try:
|
||||
legacy = cfg.get(CONFIG_SECTION_PRINT, "file_ready_dialog")
|
||||
if legacy:
|
||||
os.environ["PRINT_START_DIALOG"] = legacy
|
||||
except (configparser.NoSectionError, configparser.NoOptionError):
|
||||
pass
|
||||
if "PRINT_START_DIALOG" not in os.environ and "FILE_READY_DIALOG" in os.environ:
|
||||
os.environ["PRINT_START_DIALOG"] = os.environ["FILE_READY_DIALOG"]
|
||||
|
||||
|
||||
def migrate_env_to_config(env_path: pathlib.Path, config_path: pathlib.Path):
|
||||
"""Einmalige Migration: .env → config.ini anlegen."""
|
||||
env_vals: dict[str, str] = {}
|
||||
@@ -227,10 +243,17 @@ def save_filament_profiles(profiles: dict[int, dict]) -> bool:
|
||||
return False
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read(path, encoding="utf-8")
|
||||
# visible_vendors (Issue #41) ist kein Slot-Mapping — beim Ersetzen der
|
||||
# Sektion erhalten, sonst geht der Vendor-Filter beim Slot-Save verloren.
|
||||
preserved_vendors = None
|
||||
if cfg.has_option("filament_profiles", "visible_vendors"):
|
||||
preserved_vendors = cfg.get("filament_profiles", "visible_vendors")
|
||||
if cfg.has_section("filament_profiles"):
|
||||
cfg.remove_section("filament_profiles")
|
||||
if profiles:
|
||||
if profiles or preserved_vendors:
|
||||
cfg["filament_profiles"] = {}
|
||||
if preserved_vendors:
|
||||
cfg["filament_profiles"]["visible_vendors"] = preserved_vendors
|
||||
for slot_idx in sorted(profiles.keys()):
|
||||
entry = profiles[slot_idx] or {}
|
||||
if entry.get("vendor"):
|
||||
@@ -244,6 +267,43 @@ def save_filament_profiles(profiles: dict[int, dict]) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def list_visible_vendors() -> list[str]:
|
||||
"""Liest [filament_profiles] visible_vendors (komma-separiert) aus config.ini.
|
||||
|
||||
Vendor-Sichtbarkeitsfilter für das Slot-Profil-Dropdown (Issue #41 Option A).
|
||||
Leere Liste = keine Einschränkung (rückwärtskompatibel: alle Vendoren).
|
||||
"""
|
||||
path = _find_config_file()
|
||||
if not path:
|
||||
return []
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read(path, encoding="utf-8")
|
||||
if not cfg.has_option("filament_profiles", "visible_vendors"):
|
||||
return []
|
||||
raw = cfg.get("filament_profiles", "visible_vendors")
|
||||
return [v.strip() for v in raw.split(",") if v.strip()]
|
||||
|
||||
|
||||
def save_visible_vendors(vendors: list[str]) -> bool:
|
||||
"""Schreibt visible_vendors in [filament_profiles], ohne die Slot-Mappings
|
||||
(slot_N_*) zu verlieren. Leere Liste entfernt den Key wieder."""
|
||||
path = _find_config_file()
|
||||
if not path:
|
||||
return False
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read(path, encoding="utf-8")
|
||||
if not cfg.has_section("filament_profiles"):
|
||||
cfg.add_section("filament_profiles")
|
||||
clean = [v.strip() for v in (vendors or []) if v and v.strip()]
|
||||
if clean:
|
||||
cfg["filament_profiles"]["visible_vendors"] = ", ".join(clean)
|
||||
elif cfg.has_option("filament_profiles", "visible_vendors"):
|
||||
cfg.remove_option("filament_profiles", "visible_vendors")
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
cfg.write(f)
|
||||
return True
|
||||
|
||||
|
||||
def get(key: str, default: str = "") -> str:
|
||||
return os.environ.get(key, default)
|
||||
|
||||
@@ -259,3 +319,6 @@ DEFAULT_AMS_SLOT = get("DEFAULT_AMS_SLOT", "auto")
|
||||
AUTO_LEVELING = int(get("AUTO_LEVELING","1"))
|
||||
CAMERA_ON_PRINT = int(get("CAMERA_ON_PRINT","0"))
|
||||
WEB_UPLOAD_WARNING = int(get("WEB_UPLOAD_WARNING", "1"))
|
||||
PRINT_START_DIALOG = int(get("PRINT_START_DIALOG", get("FILE_READY_DIALOG", "1")))
|
||||
SPOOLMAN_SERVER = get("SPOOLMAN_SERVER", "")
|
||||
SPOOLMAN_SYNC_RATE = int(get("SPOOLMAN_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
|
||||
3
docker-compose.nightly.yml
Normal file
3
docker-compose.nightly.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
services:
|
||||
kx-bridge:
|
||||
image: gitea.it-drui.de/viewit/kx-bridge:nightly
|
||||
25
docker-compose.portainer-nightly.yml
Normal file
25
docker-compose.portainer-nightly.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
# KX-Bridge Nightly — Portainer Stack
|
||||
#
|
||||
# Paste this into Portainer → Stacks → Add stack → Web editor
|
||||
#
|
||||
# Uses the nightly build — may be unstable, for testing new features early.
|
||||
# For production use, see docker-compose.portainer.yml (:latest).
|
||||
|
||||
services:
|
||||
kx-bridge:
|
||||
image: gitea.it-drui.de/viewit/kx-bridge:nightly
|
||||
volumes:
|
||||
- /mnt/dockerdata/kx-nightly/config:/app/config
|
||||
- /mnt/dockerdata/kx-nightly/data:/app/data
|
||||
ports:
|
||||
# Port 7125 = first printer. Add 7126, 7127, … for each additional printer.
|
||||
- "7125:7125"
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
# Verzeichnisse müssen auf dem Host existieren:
|
||||
# mkdir -p /mnt/dockerdata/kx-nightly/config /mnt/dockerdata/kx-nightly/data
|
||||
29
docker-compose.portainer.yml
Normal file
29
docker-compose.portainer.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
# KX-Bridge — Portainer Stack
|
||||
#
|
||||
# Paste this into Portainer → Stacks → Add stack → Web editor
|
||||
#
|
||||
# No configuration needed upfront — just deploy, open http://HOST-IP:7125
|
||||
# and add your printer via the UI (IP only, credentials are fetched automatically).
|
||||
#
|
||||
# All data (config, GCode store, database) is stored in named Docker volumes
|
||||
# managed by Portainer.
|
||||
|
||||
services:
|
||||
kx-bridge:
|
||||
image: gitea.it-drui.de/viewit/kx-bridge:latest
|
||||
volumes:
|
||||
- kx-bridge-config:/app/config
|
||||
- kx-bridge-data:/app/data
|
||||
ports:
|
||||
# Port 7125 = first printer. Add 7126, 7127, … for each additional printer.
|
||||
- "7125:7125"
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "3"
|
||||
|
||||
volumes:
|
||||
kx-bridge-config:
|
||||
kx-bridge-data:
|
||||
344
docs/filament-preset-bridge-guide.md
Normal file
344
docs/filament-preset-bridge-guide.md
Normal file
@@ -0,0 +1,344 @@
|
||||
# Eigene Filament-Presets anlegen, prüfen und mit KX-Bridge verknüpfen
|
||||
|
||||
> **Gilt für:** OrcaSlicer-KX v2.4.0-alpha-kx2 oder neuer
|
||||
|
||||
---
|
||||
|
||||
## Was ist die `filament_id` und warum ist sie wichtig?
|
||||
|
||||
Jedes Filament-Preset in OrcaSlicer hat eine interne `filament_id`. Diese ID wird von der KX-Bridge genutzt, um beim AMS-Sync das richtige Preset zuzuordnen.
|
||||
|
||||
- System-Presets (z.B. "Polymaker PolyTerra PLA") haben eine feste ID wie `GFL99` oder `OGFL04`.
|
||||
- **Eigene (User-)Presets** bekommen in OrcaSlicer-KX automatisch eine eindeutige ID, die mit `P` beginnt (z.B. `P3a7f2c1`).
|
||||
|
||||
Ohne eindeutige ID zeigt OrcaSlicer beim Sync immer "Generic PLA" — auch wenn das Preset existiert.
|
||||
|
||||
---
|
||||
|
||||
## 1. Eigenes Filament-Preset anlegen
|
||||
|
||||
1. OrcaSlicer-KX starten
|
||||
2. Rechts oben im **Filament-Dropdown** ein passendes Basis-Preset wählen (z.B. "Generic PLA" oder ein Hersteller-Preset)
|
||||
3. Einstellungen nach Wunsch anpassen (Temperaturen, Kühlung, etc.)
|
||||
4. Auf das **Speichern-Symbol** (Diskette) klicken → **"Save as new preset"**
|
||||
5. Namen eingeben — z.B. `SUNLU PLA+ 2.0`
|
||||
> Der Name muss später exakt so in der Bridge eingetragen werden.
|
||||
6. Drucker auswählen: **Anycubic Kobra X 0.4 nozzle** — wichtig für die Kompatibilität!
|
||||
7. **Speichern** klicken
|
||||
8. OrcaSlicer **einmal neu starten** — erst dann wird die `filament_id` dauerhaft gespeichert.
|
||||
|
||||
---
|
||||
|
||||
## 2. Eindeutige ID prüfen
|
||||
|
||||
Nach dem Neustart prüfen, ob die ID korrekt gesetzt wurde:
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%APPDATA%\OrcaSlicer\user\default\filament\SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```
|
||||
~/.config/OrcaSlicer/user/default/filament/SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
Die Datei öffnen und nach `filament_id` suchen:
|
||||
|
||||
```json
|
||||
{
|
||||
"filament_id": "P3a7f2c1",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
✅ Korrekt: ID beginnt mit `P` gefolgt von 7 Hex-Zeichen
|
||||
❌ Fehlt oder leer: OrcaSlicer-KX zu alt — Update auf v2.4.0-alpha-kx2 oder neuer
|
||||
|
||||
---
|
||||
|
||||
## 3. Preset auf einen anderen PC übertragen (Import)
|
||||
|
||||
### Exportieren (Quell-PC)
|
||||
|
||||
Die Preset-Datei einfach kopieren:
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%APPDATA%\OrcaSlicer\user\default\filament\SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```
|
||||
~/.config/OrcaSlicer/user/default/filament/SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
### Importieren (Ziel-PC)
|
||||
|
||||
**Methode A — Datei direkt kopieren:**
|
||||
1. Die `.json`-Datei in das gleiche Verzeichnis auf dem Ziel-PC kopieren
|
||||
2. OrcaSlicer neu starten → Preset erscheint im Dropdown
|
||||
|
||||
**Methode B — OrcaSlicer Import-Funktion:**
|
||||
1. In OrcaSlicer: **File → Import → Import Configs...**
|
||||
2. Die `.json`-Datei auswählen
|
||||
3. OrcaSlicer neu starten
|
||||
|
||||
> **Wichtig:** Die `filament_id` in der Datei bleibt erhalten — das Preset wird auf dem Ziel-PC genauso erkannt wie auf dem Quell-PC.
|
||||
|
||||
---
|
||||
|
||||
## 4. Preset in KX-Bridge verknüpfen
|
||||
|
||||
1. KX-Bridge UI öffnen
|
||||
2. **Filament-Verwaltung** → AMS-Slot auswählen
|
||||
3. Im Feld **Filament-Name** exakt den OrcaSlicer-Preset-Namen eintragen:
|
||||
```
|
||||
SUNLU PLA+ 2.0
|
||||
```
|
||||
4. Speichern
|
||||
|
||||
Die Bridge sendet beim Sync `filament_name: "SUNLU PLA+ 2.0"` → OrcaSlicer findet das Preset anhand von Name und `filament_id` → zeigt es korrekt an.
|
||||
|
||||
---
|
||||
|
||||
## Wichtige Hinweise
|
||||
|
||||
| Was | Warum |
|
||||
|-----|-------|
|
||||
| Name in OrcaSlicer und Bridge müssen **exakt** übereinstimmen | Groß-/Kleinschreibung und Sonderzeichen werden verglichen |
|
||||
| Preset muss für **Anycubic Kobra X 0.4 nozzle** kompatibel sein | Beim Speichern den richtigen Drucker auswählen |
|
||||
| Nach dem ersten Speichern OrcaSlicer **neu starten** | Erst dann wird die `filament_id` persistent geschrieben |
|
||||
| **OrcaSlicer-KX v2.4.0-alpha-kx2** oder neuer verwenden | Ältere Versionen generieren keine eindeutige `filament_id` für User-Presets |
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
# How to Create, Verify and Import Custom Filament Presets for KX-Bridge
|
||||
|
||||
> **Requires:** OrcaSlicer-KX v2.4.0-alpha-kx2 or newer
|
||||
|
||||
---
|
||||
|
||||
## What is the `filament_id` and why does it matter?
|
||||
|
||||
Every filament preset in OrcaSlicer has an internal `filament_id`. The KX-Bridge uses this ID to match the correct preset during AMS sync.
|
||||
|
||||
- System presets (e.g. "Polymaker PolyTerra PLA") have a fixed ID like `GFL99` or `OGFL04`.
|
||||
- **Custom (user) presets** automatically receive a unique ID starting with `P` (e.g. `P3a7f2c1`) in OrcaSlicer-KX.
|
||||
|
||||
Without a unique ID, OrcaSlicer will always show "Generic PLA" during sync — even if the preset exists.
|
||||
|
||||
---
|
||||
|
||||
## 1. Create a Custom Filament Preset
|
||||
|
||||
1. Launch OrcaSlicer-KX
|
||||
2. Select a suitable base preset from the **filament dropdown** (e.g. "Generic PLA" or a vendor preset)
|
||||
3. Adjust settings as needed (temperatures, cooling, etc.)
|
||||
4. Click the **save icon** (floppy disk) → **"Save as new preset"**
|
||||
5. Enter a name — e.g. `SUNLU PLA+ 2.0`
|
||||
> This name must be entered in the bridge exactly as typed here.
|
||||
6. Select printer: **Anycubic Kobra X 0.4 nozzle** — required for compatibility!
|
||||
7. Click **Save**
|
||||
8. **Restart OrcaSlicer once** — the `filament_id` is only written permanently after a restart.
|
||||
|
||||
---
|
||||
|
||||
## 2. Verify the Unique ID
|
||||
|
||||
After restarting, check that the ID was set correctly:
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%APPDATA%\OrcaSlicer\user\default\filament\SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```
|
||||
~/.config/OrcaSlicer/user/default/filament/SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
Open the file and look for `filament_id`:
|
||||
|
||||
```json
|
||||
{
|
||||
"filament_id": "P3a7f2c1",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
✅ Correct: ID starts with `P` followed by 7 hex characters
|
||||
❌ Missing or empty: Your OrcaSlicer-KX version is too old — update to v2.4.0-alpha-kx2 or newer
|
||||
|
||||
---
|
||||
|
||||
## 3. Transfer a Preset to Another PC (Import)
|
||||
|
||||
### Export (source PC)
|
||||
|
||||
Simply copy the preset file:
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%APPDATA%\OrcaSlicer\user\default\filament\SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```
|
||||
~/.config/OrcaSlicer/user/default/filament/SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
### Import (target PC)
|
||||
|
||||
**Method A — Copy file directly:**
|
||||
1. Copy the `.json` file to the same directory on the target PC
|
||||
2. Restart OrcaSlicer → preset appears in the dropdown
|
||||
|
||||
**Method B — OrcaSlicer import function:**
|
||||
1. In OrcaSlicer: **File → Import → Import Configs...**
|
||||
2. Select the `.json` file
|
||||
3. Restart OrcaSlicer
|
||||
|
||||
> **Note:** The `filament_id` inside the file is preserved — the preset will be recognized on the target PC exactly as on the source PC.
|
||||
|
||||
---
|
||||
|
||||
## 4. Link the Preset in KX-Bridge
|
||||
|
||||
1. Open the KX-Bridge UI
|
||||
2. Go to **Filament Management** → select the AMS slot
|
||||
3. In the **Filament Name** field, enter the OrcaSlicer preset name exactly:
|
||||
```
|
||||
SUNLU PLA+ 2.0
|
||||
```
|
||||
4. Save
|
||||
|
||||
The bridge sends `filament_name: "SUNLU PLA+ 2.0"` during sync → OrcaSlicer matches by name and `filament_id` → displays the preset correctly.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| What | Why |
|
||||
|------|-----|
|
||||
| Name in OrcaSlicer and Bridge must match **exactly** | Case and special characters are compared |
|
||||
| Preset must be compatible with **Anycubic Kobra X 0.4 nozzle** | Select the correct printer when saving |
|
||||
| **Restart OrcaSlicer** after saving for the first time | The `filament_id` is only written persistently after a restart |
|
||||
| Use **OrcaSlicer-KX v2.4.0-alpha-kx2** or newer | Older versions do not generate a unique `filament_id` for user presets |
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
# Cómo crear, verificar e importar perfiles de filamento personalizados para KX-Bridge
|
||||
|
||||
> **Requiere:** OrcaSlicer-KX v2.4.0-alpha-kx2 o superior
|
||||
|
||||
---
|
||||
|
||||
## ¿Qué es el `filament_id` y por qué es importante?
|
||||
|
||||
Cada perfil de filamento en OrcaSlicer tiene un `filament_id` interno. KX-Bridge usa este ID para asignar el perfil correcto durante la sincronización AMS.
|
||||
|
||||
- Los perfiles del sistema (p. ej. "Polymaker PolyTerra PLA") tienen un ID fijo como `GFL99` o `OGFL04`.
|
||||
- Los **perfiles personalizados (usuario)** reciben automáticamente un ID único que empieza por `P` (p. ej. `P3a7f2c1`) en OrcaSlicer-KX.
|
||||
|
||||
Sin un ID único, OrcaSlicer mostrará siempre "Generic PLA" durante la sincronización, aunque el perfil exista.
|
||||
|
||||
---
|
||||
|
||||
## 1. Crear un perfil de filamento personalizado
|
||||
|
||||
1. Iniciar OrcaSlicer-KX
|
||||
2. Seleccionar un perfil base adecuado en el **menú desplegable de filamento** (p. ej. "Generic PLA" o un perfil de fabricante)
|
||||
3. Ajustar la configuración según sea necesario (temperaturas, refrigeración, etc.)
|
||||
4. Hacer clic en el **icono de guardar** (disquete) → **"Save as new preset"**
|
||||
5. Introducir un nombre — p. ej. `SUNLU PLA+ 2.0`
|
||||
> Este nombre debe introducirse en la bridge exactamente igual.
|
||||
6. Seleccionar impresora: **Anycubic Kobra X 0.4 nozzle** — ¡necesario para la compatibilidad!
|
||||
7. Hacer clic en **Guardar**
|
||||
8. **Reiniciar OrcaSlicer una vez** — el `filament_id` solo se escribe de forma permanente tras un reinicio.
|
||||
|
||||
---
|
||||
|
||||
## 2. Verificar el ID único
|
||||
|
||||
Tras reiniciar, comprobar que el ID se ha establecido correctamente:
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%APPDATA%\OrcaSlicer\user\default\filament\SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```
|
||||
~/.config/OrcaSlicer/user/default/filament/SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
Abrir el archivo y buscar `filament_id`:
|
||||
|
||||
```json
|
||||
{
|
||||
"filament_id": "P3a7f2c1",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
✅ Correcto: el ID empieza por `P` seguido de 7 caracteres hexadecimales
|
||||
❌ Falta o está vacío: la versión de OrcaSlicer-KX es demasiado antigua — actualizar a v2.4.0-alpha-kx2 o superior
|
||||
|
||||
---
|
||||
|
||||
## 3. Transferir un perfil a otro PC (importar)
|
||||
|
||||
### Exportar (PC de origen)
|
||||
|
||||
Simplemente copiar el archivo del perfil:
|
||||
|
||||
**Windows:**
|
||||
```
|
||||
%APPDATA%\OrcaSlicer\user\default\filament\SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```
|
||||
~/.config/OrcaSlicer/user/default/filament/SUNLU PLA+ 2.0.json
|
||||
```
|
||||
|
||||
### Importar (PC de destino)
|
||||
|
||||
**Método A — Copiar el archivo directamente:**
|
||||
1. Copiar el archivo `.json` al mismo directorio en el PC de destino
|
||||
2. Reiniciar OrcaSlicer → el perfil aparece en el menú desplegable
|
||||
|
||||
**Método B — Función de importación de OrcaSlicer:**
|
||||
1. En OrcaSlicer: **File → Import → Import Configs...**
|
||||
2. Seleccionar el archivo `.json`
|
||||
3. Reiniciar OrcaSlicer
|
||||
|
||||
> **Nota:** El `filament_id` dentro del archivo se conserva — el perfil se reconocerá en el PC de destino exactamente igual que en el de origen.
|
||||
|
||||
---
|
||||
|
||||
## 4. Vincular el perfil en KX-Bridge
|
||||
|
||||
1. Abrir la interfaz de KX-Bridge
|
||||
2. Ir a **Gestión de filamentos** → seleccionar la ranura AMS
|
||||
3. En el campo **Nombre de filamento**, introducir el nombre exacto del perfil de OrcaSlicer:
|
||||
```
|
||||
SUNLU PLA+ 2.0
|
||||
```
|
||||
4. Guardar
|
||||
|
||||
La bridge envía `filament_name: "SUNLU PLA+ 2.0"` durante la sincronización → OrcaSlicer busca por nombre y `filament_id` → muestra el perfil correctamente.
|
||||
|
||||
---
|
||||
|
||||
## Referencia rápida
|
||||
|
||||
| Qué | Por qué |
|
||||
|-----|---------|
|
||||
| El nombre en OrcaSlicer y en Bridge debe coincidir **exactamente** | Se comparan mayúsculas, minúsculas y caracteres especiales |
|
||||
| El perfil debe ser compatible con **Anycubic Kobra X 0.4 nozzle** | Seleccionar la impresora correcta al guardar |
|
||||
| **Reiniciar OrcaSlicer** tras guardar por primera vez | El `filament_id` solo se escribe de forma permanente tras un reinicio |
|
||||
| Usar **OrcaSlicer-KX v2.4.0-alpha-kx2** o superior | Las versiones anteriores no generan un `filament_id` único para perfiles de usuario |
|
||||
@@ -48,3 +48,6 @@ MODE_ID = get("MODE_ID", "")
|
||||
DEVICE_ID = get("DEVICE_ID", "")
|
||||
DEFAULT_AMS_SLOT = get("DEFAULT_AMS_SLOT", "auto")
|
||||
AUTO_LEVELING = int(get("AUTO_LEVELING", "1"))
|
||||
CAMERA_ON_PRINT = int(get("CAMERA_ON_PRINT", "0"))
|
||||
WEB_UPLOAD_WARNING = int(get("WEB_UPLOAD_WARNING", "1"))
|
||||
PRINT_START_DIALOG = int(get("PRINT_START_DIALOG", get("FILE_READY_DIALOG", "1")))
|
||||
|
||||
115
kobrax_client.py
115
kobrax_client.py
@@ -27,6 +27,7 @@ import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
import ssl
|
||||
import sys
|
||||
@@ -120,6 +121,10 @@ class KobraXClient:
|
||||
self._buf = b""
|
||||
self._pid = 1
|
||||
self._lock = threading.Lock()
|
||||
# Generations-Marker: wird bei jedem Socket-Swap/Close erhöht, damit der
|
||||
# Reader-Thread erkennt wenn _reconnect/_do_connect den Socket unter ihm
|
||||
# ersetzt hat (Issue #53). Schützt gegen recv auf einem stale fd.
|
||||
self._sock_gen = 0
|
||||
self._running = False
|
||||
|
||||
# Pending requests by msgid (for response ACK)
|
||||
@@ -167,20 +172,31 @@ class KobraXClient:
|
||||
ctx.set_ciphers("DEFAULT:@SECLEVEL=0")
|
||||
ctx.load_cert_chain(CERT_FILE, KEY_FILE)
|
||||
|
||||
raw = socket.create_connection((self.host, self.port), timeout=5)
|
||||
self._sock = ctx.wrap_socket(raw)
|
||||
log.info("TLS connected cipher=%s", self._sock.cipher()[0])
|
||||
# Socket als lokale Variable aufbauen — der Handshake (Connect + CONNACK)
|
||||
# läuft OHNE gehaltenes Lock, damit ein langsamer Connect die Sender nicht
|
||||
# einfriert. Erst der fertige Socket wird unter Lock eingeschwenkt (#53).
|
||||
_ai = socket.getaddrinfo(self.host, self.port, socket.AF_INET, socket.SOCK_STREAM)
|
||||
raw = socket.create_connection(_ai[0][4], timeout=5)
|
||||
new_sock = ctx.wrap_socket(raw)
|
||||
log.info("TLS connected cipher=%s", new_sock.cipher()[0])
|
||||
|
||||
self._sock.sendall(_build_connect(self.client_id, self.username, self.password))
|
||||
self._sock.settimeout(3)
|
||||
r = self._sock.recv(64)
|
||||
new_sock.sendall(_build_connect(self.client_id, self.username, self.password))
|
||||
new_sock.settimeout(3)
|
||||
r = new_sock.recv(64)
|
||||
if len(r) < 4 or r[0] != 0x20 or r[3] != 0:
|
||||
try:
|
||||
new_sock.close()
|
||||
except Exception:
|
||||
pass
|
||||
raise RuntimeError(f"CONNACK failed: {r.hex()}")
|
||||
log.info("CONNACK rc=0")
|
||||
|
||||
self._sock.settimeout(0.2)
|
||||
self._buf = b""
|
||||
self._subscribe(self._sub_topic())
|
||||
new_sock.settimeout(0.2)
|
||||
with self._lock:
|
||||
self._sock = new_sock
|
||||
self._sock_gen += 1
|
||||
self._buf = b""
|
||||
self._subscribe(self._sub_topic()) # nimmt das Lock selbst — nicht verschachteln
|
||||
log.debug("MQTT connected to %s:%s", self.host, self.port)
|
||||
|
||||
def connect(self):
|
||||
@@ -206,10 +222,14 @@ class KobraXClient:
|
||||
|
||||
def disconnect(self):
|
||||
self._running = False
|
||||
try:
|
||||
self._sock.close()
|
||||
except Exception:
|
||||
pass
|
||||
with self._lock:
|
||||
try:
|
||||
if self._sock is not None:
|
||||
self._sock.close()
|
||||
except Exception:
|
||||
pass
|
||||
self._sock = None
|
||||
self._sock_gen += 1
|
||||
|
||||
def _reconnect(self):
|
||||
"""Persistenter Reconnect: versucht endlos weiter bis der Drucker wieder
|
||||
@@ -218,10 +238,16 @@ class KobraXClient:
|
||||
nur DEBUG um Log-Spam bei langem Drucker-Ausfall (z.B. über Nacht
|
||||
ausgeschaltet) zu vermeiden."""
|
||||
log.warning("Verbindung verloren – reconnect…")
|
||||
try:
|
||||
self._sock.close()
|
||||
except Exception:
|
||||
pass
|
||||
# Close + Invalidierung unter Lock, damit kein Sender mitten im sendall
|
||||
# auf den gerade geschlossenen Socket trifft (Issue #53).
|
||||
with self._lock:
|
||||
try:
|
||||
if self._sock is not None:
|
||||
self._sock.close()
|
||||
except Exception:
|
||||
pass
|
||||
self._sock = None
|
||||
self._sock_gen += 1
|
||||
delays = [2, 4, 8, 15, 30, 60]
|
||||
attempt = 0
|
||||
while self._running:
|
||||
@@ -245,7 +271,8 @@ class KobraXClient:
|
||||
with self._lock:
|
||||
pid = self._pid
|
||||
self._pid += 1
|
||||
self._sock.sendall(_build_subscribe(topic, pid))
|
||||
if self._sock is not None:
|
||||
self._sock.sendall(_build_subscribe(topic, pid))
|
||||
log.info("SUB %s", topic)
|
||||
|
||||
# -- Read loop -----------------------------------------------------------
|
||||
@@ -255,17 +282,52 @@ class KobraXClient:
|
||||
_empty_count = 0
|
||||
while self._running:
|
||||
if time.time() - last_ping > 30:
|
||||
ping_ok = False
|
||||
with self._lock:
|
||||
try:
|
||||
self._sock.sendall(_build_pingreq())
|
||||
if self._sock is not None:
|
||||
self._sock.sendall(_build_pingreq())
|
||||
ping_ok = True
|
||||
except Exception:
|
||||
if self._running and not self._reconnect():
|
||||
break
|
||||
last_ping = time.time()
|
||||
continue
|
||||
ping_ok = False
|
||||
# _reconnect() AUSSERHALB des Locks aufrufen — es nimmt das Lock
|
||||
# selbst, und threading.Lock ist nicht reentrant (sonst Deadlock).
|
||||
if not ping_ok:
|
||||
if self._running and not self._reconnect():
|
||||
break
|
||||
last_ping = time.time()
|
||||
# Aktuellen Socket + Generation unter Lock greifen, damit ein
|
||||
# paralleler _reconnect/_do_connect-Swap uns nicht auf einem stale
|
||||
# fd pollen lässt (Issue #53).
|
||||
with self._lock:
|
||||
sock = self._sock
|
||||
gen = self._sock_gen
|
||||
if sock is None:
|
||||
time.sleep(0.05)
|
||||
continue
|
||||
|
||||
# Idle-Wartezeit OHNE Lock — select probt nur die Bereitschaft, so
|
||||
# blockiert der Reader während Leerlauf nie das gemeinsame Lock.
|
||||
try:
|
||||
data = self._sock.recv(65536)
|
||||
ready, _, _ = select.select([sock], [], [], 0.2)
|
||||
except (OSError, ValueError):
|
||||
# fd geschlossen/ungültig (Reconnect oder Disconnect mitten im select)
|
||||
if not self._running:
|
||||
break
|
||||
time.sleep(0.05)
|
||||
continue
|
||||
if not ready:
|
||||
continue # Leerlauf, kein Lock gehalten
|
||||
|
||||
# Daten liegen an: Lock kurz greifen für das eine recv, serialisiert
|
||||
# gegen alle sendall-Caller. recv blockiert nicht lange (select sagte
|
||||
# ready, Socket-Timeout ist 0.2s).
|
||||
try:
|
||||
with self._lock:
|
||||
# Socket könnte zwischen select und hier ersetzt worden sein.
|
||||
if self._sock_gen != gen or self._sock is not sock:
|
||||
continue
|
||||
data = sock.recv(65536)
|
||||
if not data:
|
||||
# Windows SSL kann kurzzeitig b"" liefern ohne echten EOF
|
||||
_empty_count += 1
|
||||
@@ -274,7 +336,7 @@ class KobraXClient:
|
||||
continue
|
||||
_empty_count = 0
|
||||
self._buf += data
|
||||
self._drain()
|
||||
self._drain() # außerhalb des Locks — Dispatch/event.set() bleibt prompt
|
||||
except ssl.SSLWantReadError:
|
||||
continue
|
||||
except socket.timeout:
|
||||
@@ -596,7 +658,8 @@ class KobraXClient:
|
||||
# langsamerem WLAN am Drucker dauert das Schieben sonst >30 s und
|
||||
# würde den Connect-Timeout fälschlich auslösen. Read-Timeout danach
|
||||
# generös (Drucker verarbeitet die Datei bevor er antwortet).
|
||||
sock = socket.create_connection((self.host, 18910), timeout=10)
|
||||
_ai = socket.getaddrinfo(self.host, 18910, socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock = socket.create_connection(_ai[0][4], timeout=10)
|
||||
sock.settimeout(None) # blocking während Send
|
||||
sock.sendall(headers + body)
|
||||
sock.settimeout(180)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
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
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,118 +38,17 @@
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="en">English</option>
|
||||
<option value="es">Espanol</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="it">Italiano</option>
|
||||
<option value="zh-cn">中文(简体)</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="theme-btn" onclick="openSettings()" id="settings-btn" title="Einstellungen">⚙</button>
|
||||
<button class="theme-btn" onclick="showPanel('settings')" id="settings-btn" title="Einstellungen">⚙</button>
|
||||
<button class="conn-btn disconnected" id="conn-btn" onclick="toggleConnection()">⚡ Verbinden</button>
|
||||
</header>
|
||||
|
||||
<!-- ═══ SETTINGS MODAL ═══ -->
|
||||
<div class="modal-overlay" id="settings-modal" onclick="if(event.target===this)closeSettings()">
|
||||
<div class="modal-box">
|
||||
<div class="modal-header">
|
||||
<span class="modal-title" id="modal-title-settings">Einstellungen</span>
|
||||
<button class="modal-close" onclick="closeSettings()">✕</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="modal-field" style="margin-bottom:12px">
|
||||
<label id="lbl-printer-name" style="font-weight:600">Drucker-Name</label>
|
||||
<input type="text" id="s-printer-name" placeholder="z.B. Kobra X Links">
|
||||
</div>
|
||||
<div class="modal-section" id="modal-sec-connection">Verbindung</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-printer-ip">Drucker-IP</label>
|
||||
<input type="text" id="s-printer-ip" placeholder="192.168.x.x">
|
||||
<small id="lbl-ip-hint" style="color:#f80;display:none"></small>
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-mqtt-port">MQTT-Port</label>
|
||||
<input type="number" id="s-mqtt-port" placeholder="9883">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-username">MQTT-Benutzername</label>
|
||||
<input type="text" id="s-username" placeholder="userXXXXXXXX" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-password">MQTT-Passwort</label>
|
||||
<input type="password" id="s-password" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-device-id">Device-ID</label>
|
||||
<input type="text" id="s-device-id" placeholder="32 Hex-Zeichen">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-mode-id">Mode-ID</label>
|
||||
<input type="text" id="s-mode-id" placeholder="20030">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="modal-section" id="modal-sec-print">Druckeinstellungen</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-default-slot">Standard-Slot (Einfarbdruck)</label>
|
||||
<select id="s-default-slot">
|
||||
<option value="auto" id="opt-slot-auto">Auto (alle belegten Slots)</option>
|
||||
<option value="0" id="opt-slot-0">Slot 1</option>
|
||||
<option value="1" id="opt-slot-1">Slot 2</option>
|
||||
<option value="2" id="opt-slot-2">Slot 3</option>
|
||||
<option value="3" id="opt-slot-3">Slot 4</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<input type="checkbox" id="s-auto-leveling" style="width:auto;margin:0">
|
||||
<label id="lbl-auto-leveling" style="margin:0;cursor:pointer" for="s-auto-leveling">Auto-Leveling vor Druck</label>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<input type="checkbox" id="s-camera-on-print" style="width:auto;margin:0">
|
||||
<label id="lbl-camera-on-print" style="margin:0;cursor:pointer" for="s-camera-on-print">Kamera bei Druckstart einschalten</label>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<input type="checkbox" id="s-web-upload-warning" style="width:auto;margin:0">
|
||||
<label id="lbl-web-upload-warning" style="margin:0;cursor:pointer" for="s-web-upload-warning">Warnung bei Web-Upload-Druck anzeigen</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="modal-section" id="modal-sec-poll">Poll-Intervall</div>
|
||||
<div class="poll-btns">
|
||||
<button class="poll-btn" onclick="setPoll(1000)" id="poll-1">1s</button>
|
||||
<button class="poll-btn active" onclick="setPoll(2000)" id="poll-2">2s</button>
|
||||
<button class="poll-btn" onclick="setPoll(5000)" id="poll-5">5s</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OrcaSlicer-Profile (Custom-Profile-Import, Issue #41) -->
|
||||
<div>
|
||||
<div class="modal-section" id="modal-sec-orca-profiles">OrcaSlicer-Profile</div>
|
||||
<div style="font-size:11px;color:var(--txt2);margin-bottom:8px" id="orca-profiles-hint">
|
||||
Eigene Profile aus OrcaSlicer importieren (User-Dir öffnen via Help → Show Configuration Folder)
|
||||
</div>
|
||||
<div id="orca-profiles-list" style="margin-bottom:8px;font-size:12px;color:var(--txt2)"></div>
|
||||
<button class="btn btn-sm" id="btn-orca-profiles-import" onclick="openProfileImport()"
|
||||
style="background:var(--raised);color:var(--txt)">
|
||||
⬆ <span id="lbl-orca-profiles-import">Profile importieren</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="modal-section" id="modal-sec-version">Version</div>
|
||||
<div class="update-row">
|
||||
<span id="s-version-label" style="font-size:13px;color:var(--txt)">–</span>
|
||||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="checkUpdate()" id="btn-update-check">🔄 <span id="lbl-update-check">Auf Updates prüfen</span></button>
|
||||
</div>
|
||||
<div class="update-status" id="update-status" style="margin-top:6px"></div>
|
||||
<button class="btn btn-sm btn-accent" id="btn-update-apply" style="display:none;margin-top:8px" onclick="applyUpdate()">
|
||||
<span id="lbl-update-apply">Jetzt installieren</span>
|
||||
</button>
|
||||
<div id="update-changelog" style="display:none;margin-top:10px;background:var(--raised);border-radius:6px;padding:10px;font-size:11px;font-family:var(--mono);color:var(--txt2);white-space:pre-wrap;max-height:180px;overflow-y:auto;line-height:1.6"></div>
|
||||
</div>
|
||||
|
||||
<button class="modal-save" onclick="saveSettings()" id="btn-save-settings">Speichern & Neustart</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Settings-Modal entfernt — jetzt #panel-settings (Master-Detail im Main-Bereich) -->
|
||||
|
||||
<!-- ═══ AMS SLOT EDIT DIALOG ═══ -->
|
||||
<div class="modal-overlay" id="slot-edit-modal" onclick="if(event.target===this)closeSlotEdit()">
|
||||
@@ -229,6 +128,8 @@
|
||||
<span class="nav-icon">🗂</span><span class="nav-text">Browser</span></button>
|
||||
<button class="nav-btn" onclick="showPanel('console');clearLogBadge()" id="nb-console">
|
||||
<span class="nav-icon">≡</span><span class="nav-text">Konsole</span><span id="log-badge" style="display:none;margin-left:4px;background:var(--err);color:#fff;border-radius:10px;font-size:10px;padding:1px 5px;font-weight:700"></span></button>
|
||||
<button class="nav-btn" onclick="showPanel('settings')" id="nb-settings">
|
||||
<span class="nav-icon">⚙</span><span class="nav-text" id="nav-settings">Einstellungen</span></button>
|
||||
</nav>
|
||||
|
||||
<main>
|
||||
@@ -256,6 +157,7 @@
|
||||
<div style="font-size:12px;color:#fff" id="cam-fname"></div>
|
||||
</div>
|
||||
<button class="cam-toggle" onclick="toggleCam()" id="cam-toggle-btn">▶ Kamera</button>
|
||||
<button class="cam-toggle" onclick="resetCamera()" id="cam-reset-btn" style="display:none;margin-left:4px" title="Kamera-Stream neu verbinden (nach 429-Sperre)">↺</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -266,9 +168,15 @@
|
||||
<div class="pct-big"><span id="d-pct">0</span><small>%</small></div>
|
||||
<div style="display:flex;align-items:center;gap:10px;margin:8px 0">
|
||||
<div class="progress-bar" style="flex:1;margin:0"><div class="progress-fill" id="d-pbar" style="width:0%"></div></div>
|
||||
<div class="time-block" style="padding:6px 10px;min-width:72px;text-align:center;flex-shrink:0">
|
||||
<div class="time-label" id="d-lbl-layers"></div>
|
||||
<div class="time-val" style="font-size:16px" id="d-layers">–</div>
|
||||
<div style="display:flex;flex-direction:column;gap:4px;flex-shrink:0">
|
||||
<div class="time-block" style="padding:6px 10px;min-width:72px;text-align:center">
|
||||
<div class="time-label" id="d-lbl-layers"></div>
|
||||
<div class="time-val" style="font-size:16px" id="d-layers">–</div>
|
||||
</div>
|
||||
<div class="time-block" style="padding:4px 10px;min-width:72px;text-align:center">
|
||||
<div class="time-label" id="d-lbl-zpos">Z</div>
|
||||
<div class="time-val" style="font-size:13px" id="d-zpos">–</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-grid">
|
||||
@@ -291,6 +199,12 @@
|
||||
<button class="btn btn-skip btn-sm" id="d-btn-skip" onclick="openSkipDialog()" style="display:none">✂ <span id="d-btn-skip-label">Objekte</span></button>
|
||||
<button class="btn btn-cancel btn-sm" id="d-btn-cancel" onclick="confirmCancel()">✕ Stopp</button>
|
||||
</div>
|
||||
<!-- Aktionen für eine geladene, aber nicht laufende Datei (Issue #55) -->
|
||||
<div class="ctrl-btns" id="d-idle-btns" style="margin-top:12px;display:none">
|
||||
<button class="btn btn-accent btn-sm" id="d-idle-print" onclick="startIdleFile()">▶ <span id="d-idle-print-lbl">Drucken</span></button>
|
||||
<button class="btn btn-sm" id="d-idle-slots" onclick="startIdleFileWithSlots()" style="background:var(--raised);color:var(--txt)">⚙ <span id="d-idle-slots-lbl">Slots zuordnen</span></button>
|
||||
<button class="btn btn-cancel btn-sm" id="d-idle-clear" onclick="clearIdleFile()">✕ <span id="d-idle-clear-lbl">Leeren</span></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Temperatursteuerung + Verlauf -->
|
||||
@@ -513,6 +427,198 @@
|
||||
<div class="console" id="console-log" style="height:calc(100vh - 260px);min-height:160px" onscroll="onLogScroll()"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ EINSTELLUNGEN ═══ -->
|
||||
<div class="panel" id="panel-settings">
|
||||
<div class="settings-wrap">
|
||||
<div class="settings-cats">
|
||||
<button class="set-cat active" id="setcat-connection" onclick="showSettingsCat('connection')"><span>🔌</span> <span id="setcat-lbl-connection">Verbindung</span></button>
|
||||
<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>
|
||||
|
||||
<div class="settings-content">
|
||||
<!-- Verbindung -->
|
||||
<div class="set-group active" id="setgrp-connection">
|
||||
<div class="card">
|
||||
<div class="card-title"><span>🔌</span> <span id="modal-sec-connection">Verbindung</span></div>
|
||||
<div class="modal-field" style="margin-bottom:12px">
|
||||
<label id="lbl-printer-name" style="font-weight:600">Drucker-Name</label>
|
||||
<input type="text" id="s-printer-name" placeholder="z.B. Kobra X Links">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-printer-ip">Drucker-IP</label>
|
||||
<input type="text" id="s-printer-ip" placeholder="192.168.x.x">
|
||||
<small id="lbl-ip-hint" style="color:#f80;display:none"></small>
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-mqtt-port">MQTT-Port</label>
|
||||
<input type="number" id="s-mqtt-port" placeholder="9883">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-username">MQTT-Benutzername</label>
|
||||
<input type="text" id="s-username" placeholder="userXXXXXXXX" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-password">MQTT-Passwort</label>
|
||||
<input type="password" id="s-password" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-device-id">Device-ID</label>
|
||||
<input type="text" id="s-device-id" placeholder="32 Hex-Zeichen">
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-mode-id">Mode-ID</label>
|
||||
<input type="text" id="s-mode-id" placeholder="20030">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Drucker -->
|
||||
<div class="set-group" id="setgrp-printer">
|
||||
<div class="card">
|
||||
<div class="card-title"><span>🖨</span> <span id="modal-sec-print">Druckeinstellungen</span></div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-default-slot">Standard-Slot (Einfarbdruck)</label>
|
||||
<select id="s-default-slot">
|
||||
<option value="auto" id="opt-slot-auto">Auto (alle belegten Slots)</option>
|
||||
<option value="0" id="opt-slot-0">Slot 1</option>
|
||||
<option value="1" id="opt-slot-1">Slot 2</option>
|
||||
<option value="2" id="opt-slot-2">Slot 3</option>
|
||||
<option value="3" id="opt-slot-3">Slot 4</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<input type="checkbox" id="s-auto-leveling" style="width:auto;margin:0">
|
||||
<label id="lbl-auto-leveling" style="margin:0;cursor:pointer" for="s-auto-leveling">Auto-Leveling vor Druck</label>
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-file-ready-mode">Nach Upload: Druckstart-Verhalten</label>
|
||||
<select id="s-file-ready-mode">
|
||||
<option value="1" id="opt-file-ready-dialog">Print-Dialog</option>
|
||||
<option value="0" id="opt-file-ready-banner">Print-Leiste</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<input type="checkbox" id="s-camera-on-print" style="width:auto;margin:0">
|
||||
<label id="lbl-camera-on-print" style="margin:0;cursor:pointer" for="s-camera-on-print">Kamera bei Druckstart einschalten</label>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<input type="checkbox" id="s-web-upload-warning" style="width:auto;margin:0">
|
||||
<label id="lbl-web-upload-warning" style="margin:0;cursor:pointer" for="s-web-upload-warning">Warnung bei Web-Upload-Druck anzeigen</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Darstellung -->
|
||||
<div class="set-group" id="setgrp-display">
|
||||
<div class="card">
|
||||
<div class="card-title"><span>🎨</span> <span id="setcat-lbl-display2">Darstellung</span></div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-set-lang">Sprache</label>
|
||||
<select id="s-lang-select" onchange="setLanguage(this.value)">
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="en">English</option>
|
||||
<option value="es">Espanol</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="it">Italiano</option>
|
||||
<option value="zh-cn">中文(简体)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-field" style="flex-direction:row;align-items:center;gap:10px">
|
||||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="toggleTheme()"><span id="lbl-set-theme">Hell / Dunkel umschalten</span></button>
|
||||
</div>
|
||||
<div class="modal-field">
|
||||
<label id="lbl-poll-interval">Poll-Intervall (Sekunden)</label>
|
||||
<input type="number" id="s-poll-interval" min="1" max="60" step="1" placeholder="3" oninput="onPollIntervalInput()">
|
||||
<small style="color:var(--txt2)" id="lbl-poll-hint">Wie oft die Bridge den Drucker-Status abfragt</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filament -->
|
||||
<div class="set-group" id="setgrp-filament">
|
||||
<div class="card">
|
||||
<div class="card-title"><span>🧵</span> <span id="modal-sec-orca-profiles">OrcaSlicer-Profile</span></div>
|
||||
<div style="font-size:11px;color:var(--txt2);margin-bottom:8px" id="orca-profiles-hint">
|
||||
Eigene Profile aus OrcaSlicer importieren (User-Dir öffnen via Help → Show Configuration Folder)
|
||||
</div>
|
||||
<div id="orca-profiles-list" style="margin-bottom:8px;font-size:12px;color:var(--txt2)"></div>
|
||||
<button class="btn btn-sm" id="btn-orca-profiles-import" onclick="openProfileImport()"
|
||||
style="background:var(--raised);color:var(--txt)">
|
||||
⬆ <span id="lbl-orca-profiles-import">Profile importieren</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title"><span>🎯</span> <span id="lbl-filament-mapping">Filament-Profil-Mapping (pro Slot)</span></div>
|
||||
<div style="font-size:11px;color:var(--txt2);margin-bottom:8px" id="filament-mapping-hint">
|
||||
Festes Orca-Profil pro AMS-Slot. Beim Slicer-Sync sendet die Bridge dieses Profil statt „Generic".
|
||||
</div>
|
||||
<div id="filament-mapping-list"></div>
|
||||
<button class="btn btn-sm" style="background:var(--accent);color:#fff;margin-top:8px" onclick="saveFilamentMapping()"><span id="lbl-filament-mapping-save">Mapping speichern</span></button>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title"><span>👁</span> <span id="lbl-visible-vendors">Sichtbare Hersteller (Profil-Dropdown)</span></div>
|
||||
<div style="font-size:11px;color:var(--txt2);margin-bottom:8px" id="visible-vendors-hint">
|
||||
Nur diese Hersteller erscheinen im Slot-Profil-Dropdown. Nichts ausgewählt = alle anzeigen. „Generic" und eigene Profile sind immer sichtbar.
|
||||
</div>
|
||||
<input type="text" id="vendor-filter-search" placeholder="Hersteller suchen…" oninput="renderVendorChecklist()"
|
||||
style="width:100%;padding:6px 10px;margin-bottom:8px;background:var(--raised);border:1px solid var(--border);border-radius:6px;color:var(--txt);font-size:12px">
|
||||
<div id="visible-vendors-list" style="max-height:260px;overflow-y:auto;border:1px solid var(--border);border-radius:6px;padding:8px"></div>
|
||||
<button class="btn btn-sm" style="background:var(--accent);color:#fff;margin-top:8px" onclick="saveVisibleVendors()"><span id="lbl-visible-vendors-save">Auswahl speichern</span></button>
|
||||
</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">
|
||||
<div class="card-title"><span>⚙</span> <span id="modal-sec-version">Version</span></div>
|
||||
<div class="update-row">
|
||||
<span id="s-version-label" style="font-size:13px;color:var(--txt)">–</span>
|
||||
<button class="btn btn-sm" style="background:var(--raised);color:var(--txt)" onclick="checkUpdate()" id="btn-update-check">🔄 <span id="lbl-update-check">Auf Updates prüfen</span></button>
|
||||
</div>
|
||||
<div class="update-status" id="update-status" style="margin-top:6px"></div>
|
||||
<button class="btn btn-sm btn-accent" id="btn-update-apply" style="display:none;margin-top:8px" onclick="applyUpdate()">
|
||||
<span id="lbl-update-apply">Jetzt installieren</span>
|
||||
</button>
|
||||
<div id="update-changelog" style="display:none;margin-top:10px;background:var(--raised);border-radius:6px;padding:10px;font-size:11px;font-family:var(--mono);color:var(--txt2);white-space:pre-wrap;max-height:180px;overflow-y:auto;line-height:1.6"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="modal-save" onclick="saveSettings()" id="btn-save-settings" style="margin-top:14px">Speichern & Neustart</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -521,6 +627,7 @@
|
||||
<button class="bnav-btn" onclick="showPanel('printers');loadPrinterTab()" id="bnb-printers"><span class="bnav-icon">🖨</span>Drucker</button>
|
||||
<button class="bnav-btn" onclick="showPanel('store');loadStore()" id="bnb-store"><span class="bnav-icon">🗂</span>Browser</button>
|
||||
<button class="bnav-btn" onclick="showPanel('console');clearLogBadge()" id="bnb-console"><span class="bnav-icon">≡</span>Log<span id="log-badge-bot" style="display:none;margin-left:3px;background:var(--err);color:#fff;border-radius:10px;font-size:10px;padding:1px 4px;font-weight:700"></span></button>
|
||||
<button class="bnav-btn" onclick="showPanel('settings')" id="bnb-settings"><span class="bnav-icon">⚙</span>Setup</button>
|
||||
</nav>
|
||||
|
||||
|
||||
@@ -550,9 +657,30 @@
|
||||
<p id="fd-slots-hint" style="font-size:12px;color:var(--txt2);margin-bottom:10px">GCode-Kanal → AMS-Slot zuweisen:</p>
|
||||
<div id="fd-slots" style="display:flex;flex-direction:column;gap:8px;margin-bottom:16px"></div>
|
||||
<div id="fd-objects-section" style="display:none;margin-bottom:16px">
|
||||
<p id="fd-objects-hint" style="font-size:12px;color:var(--txt2);margin-bottom:8px">Objekte überspringen (optional):</p>
|
||||
<div id="fd-objects-svg" style="display:none;background:var(--raised);border:1px solid var(--border);border-radius:8px;padding:6px;margin-bottom:8px;text-align:center"></div>
|
||||
<div id="fd-objects" style="display:flex;flex-direction:column;gap:6px;max-height:140px;overflow-y:auto"></div>
|
||||
<button type="button" id="fd-objects-toggle" onclick="toggleFdObjects()"
|
||||
style="display:flex;align-items:center;gap:8px;width:100%;padding:8px 10px;background:var(--raised);border:1px solid var(--border);border-radius:8px;color:var(--txt);cursor:pointer;font-size:12px;text-align:left">
|
||||
<span id="fd-objects-arrow" style="font-size:10px;transition:transform .15s">▶</span>
|
||||
<span>✂ <span id="fd-objects-toggle-lbl">Objekte überspringen</span></span>
|
||||
<span id="fd-objects-count" style="margin-left:auto;color:var(--txt2);font-weight:600"></span>
|
||||
</button>
|
||||
<div id="fd-objects-body" style="display:none;margin-top:8px">
|
||||
<div id="fd-objects-svg" style="display:none;background:var(--raised);border:1px solid var(--border);border-radius:8px;padding:6px;margin-bottom:8px;text-align:center"></div>
|
||||
<div id="fd-objects" style="display:flex;flex-direction:column;gap:6px;max-height:140px;overflow-y:auto"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-bottom:14px;padding:10px 12px;background:var(--raised);border-radius:8px;border:1px solid var(--border)">
|
||||
<div style="font-size:11px;font-weight:600;color:var(--txt2);margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em" id="fd-options-title">Druckoptionen</div>
|
||||
<div style="display:flex;align-items:center;gap:8px">
|
||||
<input type="checkbox" id="fd-auto-leveling" style="width:auto;margin:0">
|
||||
<label for="fd-auto-leveling" style="margin:0;cursor:pointer;font-size:13px" id="fd-lbl-auto-leveling">Auto-Leveling</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fd-spoolman-section" style="display:none;margin-bottom:16px;border-top:1px solid var(--border);padding-top:12px">
|
||||
<p style="font-size:12px;color:var(--txt2);margin-bottom:8px;display:flex;align-items:center;gap:6px">
|
||||
<span id="fd-spoolman-lbl">🧵 Spoolman</span>
|
||||
<span id="fd-spoolman-loading" style="display:none;font-size:10px">…</span>
|
||||
</p>
|
||||
<div id="fd-spoolman-rows" style="display:flex;flex-direction:column;gap:6px"></div>
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;justify-content:flex-end">
|
||||
<button id="fd-cancel" onclick="closeFilamentDialog()" style="padding:8px 16px;background:var(--raised);border:1px solid var(--border);border-radius:8px;color:var(--txt);cursor:pointer">Abbrechen</button>
|
||||
|
||||
@@ -212,6 +212,24 @@ canvas.tchart{width:100%;height:60px;display:block;border-radius:6px;background:
|
||||
.panel{display:none}
|
||||
.panel.active{display:block}
|
||||
|
||||
/* ── SETTINGS (Master-Detail) ── */
|
||||
.settings-wrap{display:grid;grid-template-columns:200px 1fr;gap:16px;align-items:start}
|
||||
.settings-cats{display:flex;flex-direction:column;gap:4px;position:sticky;top:12px}
|
||||
.set-cat{display:flex;align-items:center;gap:8px;padding:10px 12px;border-radius:8px;
|
||||
border:1px solid transparent;background:var(--raised);color:var(--txt2);cursor:pointer;
|
||||
font-size:13px;text-align:left;transition:background .15s,color .15s}
|
||||
.set-cat:hover{color:var(--txt)}
|
||||
.set-cat.active{background:var(--accent);color:#fff;border-color:var(--accent)}
|
||||
.set-group{display:none}
|
||||
.set-group.active{display:block}
|
||||
.set-group .card{margin-bottom:14px}
|
||||
@media(max-width:768px){
|
||||
.settings-wrap{grid-template-columns:1fr}
|
||||
.settings-cats{flex-direction:row;flex-wrap:wrap;position:static;overflow-x:auto}
|
||||
.set-cat{flex:1;min-width:auto;justify-content:center;padding:8px 6px;font-size:12px}
|
||||
.set-cat .nav-text{display:inline}
|
||||
}
|
||||
|
||||
/* ── FILE BROWSER UPLOAD ZONE ── */
|
||||
#store-upload-zone{
|
||||
display:flex;flex-direction:column;align-items:center;justify-content:center;
|
||||
|
||||
@@ -1,247 +1,329 @@
|
||||
{
|
||||
"header_status_standby": "Bereit",
|
||||
"header_status_printing": "Druckt",
|
||||
"header_status_complete": "Fertig",
|
||||
"header_status_error": "Fehler",
|
||||
"kobra_free": "Bereit",
|
||||
"kobra_busy": "Beschäftigt",
|
||||
"kobra_printing": "Druckt",
|
||||
"kobra_preheating": "Aufheizen",
|
||||
"kobra_auto_leveling": "Nivellierung",
|
||||
"kobra_checking": "Prüfung",
|
||||
"kobra_updated": "Aktualisierung",
|
||||
"kobra_init": "Initialisierung",
|
||||
"kobra_pausing": "Pausiert...",
|
||||
"kobra_paused": "Pausiert",
|
||||
"kobra_resuming": "Fortsetzen...",
|
||||
"kobra_resumed": "Fortgesetzt",
|
||||
"kobra_stopping": "Stoppt...",
|
||||
"kobra_stoped": "Gestoppt",
|
||||
"kobra_finished": "Abgeschlossen",
|
||||
"kobra_failed": "Fehler",
|
||||
"kobra_canceled": "Abgebrochen",
|
||||
"kobra_offline": "Offline",
|
||||
"nav_dashboard": "Dashboard",
|
||||
"nav_print": "Druck",
|
||||
"nav_temps": "Temperaturen",
|
||||
"nav_motion": "Achsen",
|
||||
"nav_ams": "AMS",
|
||||
"nav_extras": "Licht / Lüfter",
|
||||
"nav_console": "Konsole",
|
||||
"card_progress": "Fortschritt",
|
||||
"card_temps": "Temperaturen",
|
||||
"card_light_fan": "Lüfter",
|
||||
"card_speed": "Druckgeschwindigkeit",
|
||||
"card_cam": "Kamera",
|
||||
"lbl_elapsed": "Verstrichen:",
|
||||
"lbl_remaining": "Restzeit:",
|
||||
"lbl_slicer_time": "Slicer-Schätzung:",
|
||||
"lbl_layers": "Layer",
|
||||
"speed_silent": "🐢 Leise",
|
||||
"speed_normal": "⚡ Normal",
|
||||
"speed_sport": "🚀 Sport",
|
||||
"lbl_light": "💡 Licht",
|
||||
"lbl_feed": "Einziehen",
|
||||
"lbl_unload": "Ausziehen",
|
||||
"card_ace_dry": "ACE Trocknung",
|
||||
"ace_dry_auto_refill": "Auto-Nachschub",
|
||||
"ace_dry_chart": "Verlauf (Temp/Feuchte)",
|
||||
"ace_dry_current_temp": "Temperatur",
|
||||
"ace_dry_dialog_cancel": "Abbrechen",
|
||||
"ace_dry_dialog_confirm": "Bestätigen",
|
||||
"ace_dry_dialog_custom_name": "Eigener Name",
|
||||
"ace_dry_dialog_reset_default": "Auf Standard zurücksetzen",
|
||||
"ace_dry_dialog_save_restart": "Speichern & Neustart",
|
||||
"ace_dry_dialog_temp": "Temperatur (30-80°C)",
|
||||
"ace_dry_dialog_time": "Restzeit (h:m:s)",
|
||||
"ace_dry_dialog_title": "Trockner Temp/Zeit-Einstellungen",
|
||||
"ace_dry_dryer": "Trockner",
|
||||
"ace_dry_duration": "Dauer (Min)",
|
||||
"ace_dry_enable": "Trocknung aktivieren",
|
||||
"ace_dry_humidity": "Luftfeuchte",
|
||||
"ace_dry_preset_abs_asa": "ABS / ASA",
|
||||
"ace_dry_preset_custom": "Custom",
|
||||
"ace_dry_preset_pa_pc": "PA / PC",
|
||||
"ace_dry_preset_petg": "PETG",
|
||||
"ace_dry_preset_pla": "PLA",
|
||||
"ace_dry_preset_pla_plus": "PLA+",
|
||||
"ace_dry_preset_tpu": "TPU",
|
||||
"ace_dry_start": "▶ Start",
|
||||
"ace_dry_status_off": "Status: Aus",
|
||||
"ace_dry_status_on": "Status: Aktiv",
|
||||
"ace_dry_status_remaining": "Rest",
|
||||
"ace_dry_humidity": "Luftfeuchte",
|
||||
"ace_dry_current_temp": "Temperatur",
|
||||
"ace_dry_chart": "Verlauf (Temp/Feuchte)",
|
||||
"ace_dry_temp": "Temperatur (°C)",
|
||||
"ace_dry_duration": "Dauer (Min)",
|
||||
"ace_dry_start": "▶ Start",
|
||||
"ace_dry_stop": "■ Stop",
|
||||
"ace_dry_auto_refill": "Auto-Nachschub",
|
||||
"ace_dry_enable": "Trocknung aktivieren",
|
||||
"ace_dry_temp": "Temperatur (°C)",
|
||||
"ace_dry_temp_line": "Trocknungstemperatur",
|
||||
"ace_dry_time_line": "Trocknungszeit",
|
||||
"ace_dry_ui_pending": "(nur UI, Backend folgt)",
|
||||
"ace_dry_dialog_title": "Trockner Temp/Zeit-Einstellungen",
|
||||
"ace_dry_dialog_temp": "Temperatur (30-80°C)",
|
||||
"ace_dry_dialog_time": "Restzeit (h:m:s)",
|
||||
"ace_dry_dialog_confirm": "Bestätigen",
|
||||
"ace_dry_dialog_cancel": "Abbrechen",
|
||||
"ace_dry_dialog_save_restart": "Speichern & Neustart",
|
||||
"ace_dry_dialog_custom_name": "Eigener Name",
|
||||
"ace_dry_dialog_reset_default": "Auf Standard zurücksetzen",
|
||||
"cam_placeholder": "📷 Kamera nicht gestartet",
|
||||
"cam_stream_unavailable": "Stream nicht verfügbar",
|
||||
"add_printer": "Drucker hinzufügen",
|
||||
"ams_empty": "Leer",
|
||||
"ams_no_data": "Keine AMS-Daten empfangen",
|
||||
"apd_cancel": "Abbrechen",
|
||||
"apd_confirm": "Hinzufügen",
|
||||
"apd_err_ip": "Bitte IP-Adresse eingeben",
|
||||
"apd_fetching": "Hole Daten vom Drucker…",
|
||||
"apd_lbl_ip": "Drucker-IP",
|
||||
"apd_lbl_name": "Name (optional)",
|
||||
"apd_placeholder_name": "z.B. Kobra X Wohnzimmer",
|
||||
"apd_success": "Drucker hinzugefügt, Bridge startet neu…",
|
||||
"apd_title": "Drucker hinzufügen",
|
||||
"btn_cam_start": "▶ Kamera",
|
||||
"btn_cam_start2": "▶ Start",
|
||||
"btn_cam_stop": "◼ Kamera",
|
||||
"btn_cam_stop2": "◼ Stop",
|
||||
"btn_cancel": "✕ Stopp",
|
||||
"btn_cancel_generic": "Abbrechen",
|
||||
"btn_confirm_generic": "Bestätigen",
|
||||
"btn_connect": "⚡ Verbinden",
|
||||
"btn_delete": "Löschen",
|
||||
"btn_disable_motors": "Motoren aus",
|
||||
"btn_disconnect": "✕ Trennen",
|
||||
"btn_home_all": "Home All",
|
||||
"btn_home_xy": "Home XY",
|
||||
"btn_home_z": "Home Z",
|
||||
"btn_pause": "⏸ Pause",
|
||||
"btn_resume": "▶ Weiter",
|
||||
"btn_cancel": "✕ Stopp",
|
||||
"label_nozzle": "Düse",
|
||||
"cam_placeholder": "📷 Kamera nicht gestartet",
|
||||
"cam_stream_unavailable": "Stream nicht verfügbar",
|
||||
"card_ace_dry": "ACE Trocknung",
|
||||
"card_ams": "Filament",
|
||||
"card_cam": "Kamera",
|
||||
"card_light_fan": "Lüfter",
|
||||
"card_progress": "Fortschritt",
|
||||
"card_speed": "Druckgeschwindigkeit",
|
||||
"card_temps": "Temperaturen",
|
||||
"confirm_cancel": "Druck wirklich abbrechen?",
|
||||
"fd_cancel": "Abbrechen",
|
||||
"fd_no_matching_material": "Kein passendes Material",
|
||||
"fd_no_slots_msg": "Keine belegten AMS-Slots.{br}Druck trotzdem starten?",
|
||||
"fd_objects_hint": "Objekte überspringen (optional):",
|
||||
"fd_objects_toggle": "Objekte überspringen",
|
||||
"fd_options_title": "Optionen",
|
||||
"fd_print": "▶ Drucken",
|
||||
"fd_slot": "Slot",
|
||||
"fd_slots_hint": "GCode-Kanal → AMS-Slot zuweisen:",
|
||||
"fd_title": "Slot-Zuweisung",
|
||||
"fd_used": "BELEGT",
|
||||
"file_cancel_btn": "✕ Abbrechen",
|
||||
"file_ready_btn": "▶ Druck starten",
|
||||
"file_slots_btn": "🎨 Slots wählen",
|
||||
"header_status_complete": "Fertig",
|
||||
"header_status_error": "Fehler",
|
||||
"header_status_printing": "Druckt",
|
||||
"header_status_standby": "Bereit",
|
||||
"hint_ip_no_port": "Nur IP-Adresse, kein Port (z.B. 192.168.1.102)",
|
||||
"kobra_auto_leveling": "Nivellierung",
|
||||
"kobra_busy": "Beschäftigt",
|
||||
"kobra_canceled": "Abgebrochen",
|
||||
"kobra_checking": "Prüfung",
|
||||
"kobra_failed": "Fehler",
|
||||
"kobra_finished": "Abgeschlossen",
|
||||
"kobra_free": "Bereit",
|
||||
"kobra_init": "Initialisierung",
|
||||
"kobra_offline": "Offline",
|
||||
"kobra_paused": "Pausiert",
|
||||
"kobra_pausing": "Pausiert...",
|
||||
"kobra_preheating": "Aufheizen",
|
||||
"kobra_printing": "Druckt",
|
||||
"kobra_resumed": "Fortgesetzt",
|
||||
"kobra_resuming": "Fortsetzen...",
|
||||
"kobra_stoped": "Gestoppt",
|
||||
"kobra_stopping": "Stoppt...",
|
||||
"kobra_updated": "Aktualisierung",
|
||||
"label_bed": "Bett",
|
||||
"label_fan": "🌀 Lüfter",
|
||||
"label_light": "💡 Licht",
|
||||
"label_on_off": "Ein / Aus",
|
||||
"label_speed": "Geschwindigkeit",
|
||||
"panel_print_title": "Drucksteuerung",
|
||||
"panel_print_btn_pause": "⏸ Pause",
|
||||
"panel_print_btn_resume": "▶ Fortsetzen",
|
||||
"panel_print_btn_cancel": "✕ Abbrechen",
|
||||
"panel_print_temps_live": "Temperaturen (Live)",
|
||||
"label_set": "Setzen",
|
||||
"label_nozzle": "Düse",
|
||||
"label_off": "Aus",
|
||||
"panel_temps_nozzle": "Düse",
|
||||
"panel_temps_bed": "Heizbett",
|
||||
"panel_temps_chart": "Verlauf (letzte 60 Messungen)",
|
||||
"label_target_c": "Ziel:",
|
||||
"panel_motion_xy": "XY-Achsen",
|
||||
"panel_motion_z": "Z-Achse",
|
||||
"label_step": "Schrittweite:",
|
||||
"btn_home_z": "Home Z",
|
||||
"btn_home_xy": "Home XY",
|
||||
"btn_home_all": "Home All",
|
||||
"btn_disable_motors": "Motoren aus",
|
||||
"panel_ams_title": "Filament",
|
||||
"card_ams": "Filament",
|
||||
"ams_no_data": "Keine AMS-Daten empfangen",
|
||||
"label_on_off": "Ein / Aus",
|
||||
"label_set": "Setzen",
|
||||
"label_slot": "Slot",
|
||||
"ams_empty": "Leer",
|
||||
"panel_extras_light": "Licht",
|
||||
"panel_extras_fan": "Lüfter",
|
||||
"panel_extras_camera": "Kamera",
|
||||
"btn_cam_start2": "▶ Start",
|
||||
"btn_cam_stop2": "◼ Stop",
|
||||
"panel_console_title": "Ereignis-Log",
|
||||
"log_light_on": "Licht an",
|
||||
"log_light_off": "Licht aus",
|
||||
"log_fan": "Lüfter →",
|
||||
"log_nozzle": "Düse →",
|
||||
"log_bed": "Bett →",
|
||||
"label_speed": "Geschwindigkeit",
|
||||
"label_step": "Schrittweite:",
|
||||
"label_target_c": "Ziel:",
|
||||
"lbl_conn_error": "Verbindungsfehler:",
|
||||
"lbl_elapsed": "Verstrichen:",
|
||||
"lbl_feed": "Einziehen",
|
||||
"lbl_layers": "Layer",
|
||||
"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",
|
||||
"log_axis": "Achse",
|
||||
"log_home": "Home",
|
||||
"log_home_all": "Home All",
|
||||
"log_bed": "Bett →",
|
||||
"log_cam_start": "Kamera gestartet:",
|
||||
"log_cam_stop": "Kamera gestoppt",
|
||||
"log_poll_error": "Poll-Fehler:",
|
||||
"log_clear": "✕ Leeren",
|
||||
"log_delete_failed": "Löschung fehlgeschlagen",
|
||||
"log_dir_all": "Alle",
|
||||
"log_dir_label": "Richtung:",
|
||||
"log_dir_rx": "RX",
|
||||
"log_dir_tx": "TX",
|
||||
"log_download": "⬇ Download",
|
||||
"log_error": "Fehler:",
|
||||
"confirm_cancel": "Druck wirklich abbrechen?",
|
||||
"settings_title": "Einstellungen",
|
||||
"settings_connection": "Verbindung",
|
||||
"settings_print": "Druckeinstellungen",
|
||||
"settings_poll": "Poll-Intervall",
|
||||
"settings_version": "Version",
|
||||
"settings_save": "Speichern & Neustart",
|
||||
"settings_printer_name": "Drucker-Name",
|
||||
"settings_printer_ip": "Drucker-IP",
|
||||
"settings_mqtt_port": "MQTT-Port",
|
||||
"settings_username": "MQTT-Benutzername",
|
||||
"settings_password": "MQTT-Passwort",
|
||||
"settings_device_id": "Device-ID",
|
||||
"settings_mode_id": "Mode-ID",
|
||||
"hint_ip_no_port": "Nur IP-Adresse, kein Port (z.B. 192.168.1.102)",
|
||||
"settings_default_slot": "Standard-Slot (Einfarbdruck)",
|
||||
"settings_slot_auto": "Auto (alle belegten Slots)",
|
||||
"settings_auto_leveling": "Auto-Leveling vor Druck",
|
||||
"settings_camera_on_print": "Kamera bei Druckstart einschalten",
|
||||
"settings_web_upload_warning": "Warnung bei Web-Upload-Druck anzeigen",
|
||||
"update_check": "Auf Updates prüfen",
|
||||
"update_checking": "Prüfe...",
|
||||
"update_available": "verfügbar",
|
||||
"update_none": "Bereits aktuell",
|
||||
"update_apply": "Jetzt installieren",
|
||||
"update_applying": "Lade herunter...",
|
||||
"update_restarting": "Starte neu...",
|
||||
"update_error": "Fehler",
|
||||
"btn_connect": "⚡ Verbinden",
|
||||
"btn_disconnect": "✕ Trennen",
|
||||
"lbl_conn_error": "Verbindungsfehler:",
|
||||
"slot_edit_title": "Slot bearbeiten",
|
||||
"slot_edit_color": "Farbe",
|
||||
"slot_edit_material": "Material",
|
||||
"slot_edit_load": "⬇ Einziehen",
|
||||
"slot_edit_unload": "⬆ Ausziehen",
|
||||
"slot_edit_save": "💾 Speichern",
|
||||
"slot_edit_custom": "z.B. PLA, PETG, ABS…",
|
||||
"slot_edit_ok": "AMS Slot",
|
||||
"slot_edit_profile": "OrcaSlicer-Profil",
|
||||
"slot_edit_profile_hint": "Sendet beim OrcaSlicer-Sync die konkrete Marke statt nur „Generic\"",
|
||||
"slot_edit_profile_default": "— Generic (Default) —",
|
||||
"orca_profile_section": "OrcaSlicer-Profile",
|
||||
"log_fan": "Lüfter →",
|
||||
"log_filter_placeholder": "Filtern…",
|
||||
"log_home": "Home",
|
||||
"log_home_all": "Home All",
|
||||
"log_light_off": "Licht aus",
|
||||
"log_light_on": "Licht an",
|
||||
"log_lvl_err": "⛔ Fehler",
|
||||
"log_lvl_label": "Level:",
|
||||
"log_lvl_warn": "⚠ Warnung",
|
||||
"log_nozzle": "Düse →",
|
||||
"log_poll_error": "Poll-Fehler:",
|
||||
"log_print_action": "Druck:",
|
||||
"log_print_start": "Druckstart:",
|
||||
"log_topic_ams": "AMS",
|
||||
"log_topic_info": "Info",
|
||||
"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",
|
||||
"nav_dashboard": "Dashboard",
|
||||
"nav_extras": "Licht / Lüfter",
|
||||
"nav_motion": "Achsen",
|
||||
"nav_print": "Druck",
|
||||
"nav_printers": "Drucker",
|
||||
"nav_settings": "Einstellungen",
|
||||
"nav_temps": "Temperaturen",
|
||||
"orca_profile_done": "Importiert",
|
||||
"orca_profile_dropmsg": "Hierher ziehen oder klicken",
|
||||
"orca_profile_help_html": "Lade ein <b>ZIP</b> deines OrcaSlicer-Filament-Ordners oder einzelne <b>.json</b>-Files hoch.<br>In OrcaSlicer: <i>Help → Show Configuration Folder → user/<id>/filament/</i>",
|
||||
"orca_profile_hint": "Eigene Profile aus OrcaSlicer importieren (User-Dir öffnen via Help → Show Configuration Folder)",
|
||||
"orca_profile_import_btn": "Profile importieren",
|
||||
"orca_profile_import_link": "★ Eigene Profile importieren…",
|
||||
"orca_profile_import_title": "Eigene OrcaSlicer-Profile importieren",
|
||||
"orca_profile_help_html": "Lade ein <b>ZIP</b> deines OrcaSlicer-Filament-Ordners oder einzelne <b>.json</b>-Files hoch.<br>In OrcaSlicer: <i>Help → Show Configuration Folder → user/<id>/filament/</i>",
|
||||
"orca_profile_dropmsg": "Hierher ziehen oder klicken",
|
||||
"orca_profile_list_label": "Aktuell importiert",
|
||||
"orca_profile_user_label": "Eigene Profile",
|
||||
"orca_profile_user_empty": "– keine –",
|
||||
"orca_profile_uploading": "Lade hoch…",
|
||||
"orca_profile_done": "Importiert",
|
||||
"orca_profile_section": "OrcaSlicer-Profile",
|
||||
"orca_profile_skipped": "übersprungen",
|
||||
"log_dir_all": "Alle",
|
||||
"log_lvl_label": "Level:",
|
||||
"file_ready_btn": "▶ Druck starten",
|
||||
"file_slots_btn": "🎨 Slots wählen",
|
||||
"file_cancel_btn": "✕ Abbrechen",
|
||||
"nav_printers": "Drucker",
|
||||
"skip_title": "✂ Objekte überspringen",
|
||||
"skip_hint": "Objekte abwählen, die nicht weiter gedruckt werden sollen:",
|
||||
"skip_btn_label": "Objekte",
|
||||
"skip_no_objects": "Keine Objekte in diesem Druck.",
|
||||
"orca_profile_uploading": "Lade hoch…",
|
||||
"orca_profile_user_empty": "– keine –",
|
||||
"orca_profile_user_label": "Eigene Profile",
|
||||
"panel_ams_title": "Filament",
|
||||
"panel_browser_title": "Datei-Browser",
|
||||
"panel_console_title": "Ereignis-Log",
|
||||
"panel_extras_camera": "Kamera",
|
||||
"panel_extras_fan": "Lüfter",
|
||||
"panel_extras_light": "Licht",
|
||||
"panel_motion_xy": "XY-Achsen",
|
||||
"panel_motion_z": "Z-Achse",
|
||||
"panel_print_btn_cancel": "✕ Abbrechen",
|
||||
"panel_print_btn_pause": "⏸ Pause",
|
||||
"panel_print_btn_resume": "▶ Fortsetzen",
|
||||
"panel_print_temps_live": "Temperaturen (Live)",
|
||||
"panel_print_title": "Drucksteuerung",
|
||||
"panel_temps_bed": "Heizbett",
|
||||
"panel_temps_chart": "Verlauf (letzte 60 Messungen)",
|
||||
"panel_temps_nozzle": "Düse",
|
||||
"print_auto_leveling": "Auto-Leveling für diesen Druck",
|
||||
"printers_active": "● aktiv",
|
||||
"printers_current": "Aktueller Drucker",
|
||||
"printers_empty_hint": "Noch kein Drucker eingerichtet.",
|
||||
"printers_loading": "Lade…",
|
||||
"printers_none": "Keine Drucker konfiguriert.",
|
||||
"printers_remove": "Drucker entfernen",
|
||||
"printers_remove_confirm": "Drucker \"{name}\" entfernen? Die Bridge startet neu.",
|
||||
"printers_switch": "Wechseln →",
|
||||
"progress_action_clear": "Leeren",
|
||||
"progress_action_print": "Drucken",
|
||||
"progress_action_slots": "Slots zuordnen",
|
||||
"settings_auto_leveling": "Auto-Leveling vor Druck",
|
||||
"settings_auto_leveling_label": "Auto-Leveling vor dem Druck",
|
||||
"settings_btn_tooltip": "Einstellungen",
|
||||
"settings_camera_on_print": "Kamera bei Druckstart einschalten",
|
||||
"settings_cat_connection": "Verbindung",
|
||||
"settings_cat_display": "Darstellung",
|
||||
"settings_cat_filament": "Filament",
|
||||
"settings_cat_language": "Sprache",
|
||||
"settings_cat_printer": "Drucker",
|
||||
"settings_cat_system": "System",
|
||||
"settings_cat_theme": "Hell / Dunkel umschalten",
|
||||
"settings_connection": "Verbindung",
|
||||
"settings_default_slot": "Standard-Slot (Einfarbdruck)",
|
||||
"settings_device_id": "Device-ID",
|
||||
"settings_device_id_hint": "32 Hexzeichen",
|
||||
"settings_device_id_placeholder": "32 Hexzeichen",
|
||||
"settings_filament_mapping": "Filament-Profil-Mapping (pro Slot)",
|
||||
"settings_filament_mapping_hint": "Festes Orca-Profil pro AMS-Slot. Bei der Slicer-Synchronisierung sendet die Bridge dieses Profil statt \"Generic\".",
|
||||
"settings_filament_mapping_label": "Filament-Profil-Mapping (pro Slot)",
|
||||
"settings_filament_mapping_save": "Mapping speichern",
|
||||
"settings_filament_mapping_save_label": "Mapping speichern",
|
||||
"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",
|
||||
"settings_mqtt_port": "MQTT-Port",
|
||||
"settings_mqtt_username_placeholder": "userXXXXXXXX",
|
||||
"settings_orca_profiles_import": "Profile importieren",
|
||||
"settings_orca_profiles_label": "OrcaSlicer-Profile",
|
||||
"settings_password": "MQTT-Passwort",
|
||||
"settings_poll": "Poll-Intervall (Sekunden)",
|
||||
"settings_poll_interval_hint": "Wie oft die Bridge den Druckerstatus abfragt",
|
||||
"settings_poll_interval_label": "Poll-Intervall (Sekunden)",
|
||||
"settings_print": "Druckeinstellungen",
|
||||
"settings_printer_ip": "Drucker-IP",
|
||||
"settings_printer_name": "Drucker-Name",
|
||||
"settings_printer_name_placeholder": "z.B. Kobra X Links",
|
||||
"settings_save": "Speichern & Neustart",
|
||||
"settings_slot_auto": "Auto (alle belegten Slots)",
|
||||
"settings_theme_toggle": "Wechsel Hell / Dunkel",
|
||||
"settings_title": "Einstellungen",
|
||||
"settings_username": "MQTT-Benutzername",
|
||||
"settings_vendor_filter_placeholder": "Hersteller suchen…",
|
||||
"settings_version": "Version",
|
||||
"settings_visible_vendors": "Sichtbare Hersteller (Profil-Dropdown)",
|
||||
"settings_visible_vendors_hint": "Nur diese Hersteller erscheinen im Slot-Profil-Dropdown. Nichts ausgewählt = alle anzeigen. „Generic\" und eigene Profile sind immer sichtbar.",
|
||||
"settings_visible_vendors_label": "Sichtbare Hersteller (Profil-Dropdown)",
|
||||
"settings_visible_vendors_save": "Auswahl speichern",
|
||||
"settings_visible_vendors_save_label": "Auswahl speichern",
|
||||
"settings_web_upload_warning": "Warnung bei Web-Upload-Druck anzeigen",
|
||||
"sf_all": "Alle",
|
||||
"sf_err": "✗ Fehler",
|
||||
"sf_new": "Neu",
|
||||
"sf_ok": "✓ Erfolgreich",
|
||||
"skip_already": "übersprungen",
|
||||
"skip_btn_label": "Objekte",
|
||||
"skip_cancel": "Abbrechen",
|
||||
"skip_confirm": "Überspringen",
|
||||
"skip_confirm_btn": "Überspringen",
|
||||
"skip_hint": "Objekte abwählen, die nicht weiter gedruckt werden sollen:",
|
||||
"skip_no_objects": "Keine Objekte in diesem Druck.",
|
||||
"skip_select_at_least_one": "Bitte mindestens ein Objekt wählen.",
|
||||
"skip_sending": "Sende …",
|
||||
"skip_success": "Objekte werden übersprungen.",
|
||||
"fd_objects_hint": "Objekte überspringen (optional):",
|
||||
"fd_slots_hint": "GCode-Kanal → AMS-Slot zuweisen:",
|
||||
"fd_cancel": "Abbrechen",
|
||||
"fd_print": "▶ Drucken",
|
||||
"fd_no_slots_msg": "Keine belegten AMS-Slots.{br}Druck trotzdem starten?",
|
||||
"fd_slot": "Slot",
|
||||
"fd_no_matching_material": "Kein passendes Material",
|
||||
"fd_used": "BELEGT",
|
||||
"add_printer": "Drucker hinzufügen",
|
||||
"apd_lbl_ip": "Drucker-IP",
|
||||
"apd_lbl_name": "Name (optional)",
|
||||
"apd_placeholder_name": "z.B. Kobra X Wohnzimmer",
|
||||
"apd_cancel": "Abbrechen",
|
||||
"apd_confirm": "Hinzufügen",
|
||||
"apd_fetching": "Hole Daten vom Drucker…",
|
||||
"apd_success": "Drucker hinzugefügt, Bridge startet neu…",
|
||||
"apd_err_ip": "Bitte IP-Adresse eingeben",
|
||||
"printers_remove": "Drucker entfernen",
|
||||
"printers_remove_confirm": "Drucker \"{name}\" entfernen? Die Bridge startet neu.",
|
||||
"printers_active": "● aktiv",
|
||||
"printers_switch": "Wechseln →",
|
||||
"printers_current": "Aktueller Drucker",
|
||||
"printers_loading": "Lade…",
|
||||
"printers_none": "Keine Drucker konfiguriert.",
|
||||
"printers_empty_hint": "Noch kein Drucker eingerichtet.",
|
||||
"nav_browser": "Browser",
|
||||
"panel_browser_title": "Datei-Browser",
|
||||
"store_search_placeholder": "🔍 Suche…",
|
||||
"store_empty": "Noch keine Dateien hochgeladen.",
|
||||
"store_refresh": "↻ Aktualisieren",
|
||||
"store_print": "▶ Drucken",
|
||||
"store_download": "⬇ Download",
|
||||
"store_delete_confirm": "Datei löschen?",
|
||||
"store_print_confirm": "Datei drucken?",
|
||||
"store_web_verify_title": "Datei verifizieren",
|
||||
"store_web_verify_msg": "Bitte bestätige, dass diese Datei für den Anycubic Kobra X erstellt wurde.",
|
||||
"store_web_verify_confirm": "Bestätigen",
|
||||
"store_web_verify_abort": "Abbrechen",
|
||||
"store_no_results": "Keine Dateien gefunden.",
|
||||
"store_never": "noch nicht gedruckt",
|
||||
"store_estimate": "Schätzung",
|
||||
"store_upload_label_prefix": "GCode hierher ziehen oder ",
|
||||
"store_upload_label_browse": "durchsuchen",
|
||||
"store_upload_busy": "⏳ Hochladen…",
|
||||
"store_upload_success": "✓ {file}",
|
||||
"store_upload_error": "✗ {error}",
|
||||
"sf_all": "Alle",
|
||||
"sf_ok": "✓ Erfolgreich",
|
||||
"sf_err": "✗ Fehler",
|
||||
"sf_new": "Neu",
|
||||
"skip_title": "✂ Objekte überspringen",
|
||||
"slot_edit_color": "Farbe",
|
||||
"slot_edit_custom": "z.B. PLA, PETG, ABS…",
|
||||
"slot_edit_load": "⬇ Einziehen",
|
||||
"slot_edit_material": "Material",
|
||||
"slot_edit_ok": "AMS Slot",
|
||||
"slot_edit_profile": "OrcaSlicer-Profil",
|
||||
"slot_edit_profile_default": "— Generic (Default) —",
|
||||
"slot_edit_profile_hint": "Sendet beim OrcaSlicer-Sync die konkrete Marke statt nur „Generic\"",
|
||||
"slot_edit_save": "💾 Speichern",
|
||||
"slot_edit_title": "Slot bearbeiten",
|
||||
"slot_edit_unload": "⬆ Ausziehen",
|
||||
"speed_normal": "⚡ Normal",
|
||||
"speed_silent": "🐢 Leise",
|
||||
"speed_sport": "🚀 Sport",
|
||||
"ss_date": "↓ Datum",
|
||||
"ss_dur": "⏱ Druckzeit",
|
||||
"ss_name": "A–Z Name",
|
||||
"ss_dur": "⏱ Druckzeit"
|
||||
"store_delete_confirm": "Datei löschen?",
|
||||
"store_download": "⬇ Download",
|
||||
"store_empty": "Noch keine Dateien hochgeladen.",
|
||||
"store_estimate": "Schätzung",
|
||||
"store_never": "noch nicht gedruckt",
|
||||
"store_no_results": "Keine Dateien gefunden.",
|
||||
"store_print": "▶ Drucken",
|
||||
"store_print_confirm": "Datei drucken?",
|
||||
"store_refresh": "↻ Aktualisieren",
|
||||
"store_search_placeholder": "🔍 Suche…",
|
||||
"store_upload_busy": "⏳ Hochladen…",
|
||||
"store_upload_error": "✗ {error}",
|
||||
"store_upload_label_browse": "durchsuchen",
|
||||
"store_upload_label_prefix": "GCode hierher ziehen oder ",
|
||||
"store_upload_only_gcode": "✗ Nur GCode-Dateien erlaubt (.gcode, .3mf, .bgcode)",
|
||||
"store_upload_success": "✓ {file}",
|
||||
"store_web_verify_abort": "Abbrechen",
|
||||
"store_web_verify_confirm": "Bestätigen",
|
||||
"store_web_verify_msg": "Bitte bestätige, dass diese Datei für den Anycubic Kobra X erstellt wurde.",
|
||||
"store_web_verify_title": "Datei verifizieren",
|
||||
"update_apply": "Jetzt installieren",
|
||||
"update_applying": "Lade herunter...",
|
||||
"update_available": "verfügbar",
|
||||
"update_check": "Auf Updates prüfen",
|
||||
"update_checking": "Prüfe...",
|
||||
"update_docker": "Befehl kopieren",
|
||||
"update_docker_copied": "Kopiert! Ausführen: docker compose pull && docker compose up -d",
|
||||
"update_error": "Fehler",
|
||||
"update_none": "Bereits aktuell",
|
||||
"update_restarting": "Starte neu..."
|
||||
}
|
||||
|
||||
@@ -1,247 +1,329 @@
|
||||
{
|
||||
"header_status_standby": "Ready",
|
||||
"header_status_printing": "Printing",
|
||||
"header_status_complete": "Complete",
|
||||
"header_status_error": "Error",
|
||||
"kobra_free": "Ready",
|
||||
"kobra_busy": "Busy",
|
||||
"kobra_printing": "Printing",
|
||||
"kobra_preheating": "Preheating",
|
||||
"kobra_auto_leveling": "Auto Leveling",
|
||||
"kobra_checking": "Checking",
|
||||
"kobra_updated": "Updating",
|
||||
"kobra_init": "Initializing",
|
||||
"kobra_pausing": "Pausing...",
|
||||
"kobra_paused": "Paused",
|
||||
"kobra_resuming": "Resuming...",
|
||||
"kobra_resumed": "Resumed",
|
||||
"kobra_stopping": "Stopping...",
|
||||
"kobra_stoped": "Stopped",
|
||||
"kobra_finished": "Finished",
|
||||
"kobra_failed": "Error",
|
||||
"kobra_canceled": "Cancelled",
|
||||
"kobra_offline": "Offline",
|
||||
"nav_dashboard": "Dashboard",
|
||||
"nav_print": "Print",
|
||||
"nav_temps": "Temperatures",
|
||||
"nav_motion": "Motion",
|
||||
"nav_ams": "AMS",
|
||||
"nav_extras": "Light / Fan",
|
||||
"nav_console": "Console",
|
||||
"card_progress": "Progress",
|
||||
"card_temps": "Temperatures",
|
||||
"card_light_fan": "Fan",
|
||||
"card_speed": "Print Speed",
|
||||
"card_cam": "Camera",
|
||||
"lbl_elapsed": "Elapsed:",
|
||||
"lbl_remaining": "Remaining:",
|
||||
"lbl_slicer_time": "Slicer estimate:",
|
||||
"lbl_layers": "Layer",
|
||||
"speed_silent": "🐢 Silent",
|
||||
"speed_normal": "⚡ Normal",
|
||||
"speed_sport": "🚀 Sport",
|
||||
"lbl_light": "💡 Light",
|
||||
"lbl_feed": "Load",
|
||||
"lbl_unload": "Unload",
|
||||
"card_ace_dry": "ACE Drying",
|
||||
"ace_dry_auto_refill": "Auto Refill",
|
||||
"ace_dry_chart": "History (Temp/Humidity)",
|
||||
"ace_dry_current_temp": "Temperature",
|
||||
"ace_dry_dialog_cancel": "Cancel",
|
||||
"ace_dry_dialog_confirm": "Confirm",
|
||||
"ace_dry_dialog_custom_name": "Custom Name",
|
||||
"ace_dry_dialog_reset_default": "Reset to Default",
|
||||
"ace_dry_dialog_save_restart": "Save & Restart",
|
||||
"ace_dry_dialog_temp": "Temperature (30-80°C)",
|
||||
"ace_dry_dialog_time": "Rem. Time (h:m:s)",
|
||||
"ace_dry_dialog_title": "Dryer Temp/Time Settings",
|
||||
"ace_dry_dryer": "Dryer",
|
||||
"ace_dry_duration": "Duration (min)",
|
||||
"ace_dry_enable": "Enable Drying",
|
||||
"ace_dry_humidity": "Humidity",
|
||||
"ace_dry_preset_abs_asa": "ABS / ASA",
|
||||
"ace_dry_preset_custom": "Custom",
|
||||
"ace_dry_preset_pa_pc": "PA / PC",
|
||||
"ace_dry_preset_petg": "PETG",
|
||||
"ace_dry_preset_pla": "PLA",
|
||||
"ace_dry_preset_pla_plus": "PLA+",
|
||||
"ace_dry_preset_tpu": "TPU",
|
||||
"ace_dry_start": "▶ Start",
|
||||
"ace_dry_status_off": "Status: Off",
|
||||
"ace_dry_status_on": "Status: Active",
|
||||
"ace_dry_status_remaining": "Remaining",
|
||||
"ace_dry_humidity": "Humidity",
|
||||
"ace_dry_current_temp": "Temperature",
|
||||
"ace_dry_chart": "History (Temp/Humidity)",
|
||||
"ace_dry_temp": "Temperature (°C)",
|
||||
"ace_dry_duration": "Duration (min)",
|
||||
"ace_dry_start": "▶ Start",
|
||||
"ace_dry_stop": "■ Stop",
|
||||
"ace_dry_auto_refill": "Auto Refill",
|
||||
"ace_dry_enable": "Enable Drying",
|
||||
"ace_dry_temp": "Temperature (°C)",
|
||||
"ace_dry_temp_line": "Drying Temperature",
|
||||
"ace_dry_time_line": "Drying Time",
|
||||
"ace_dry_ui_pending": "(UI only, backend next)",
|
||||
"ace_dry_dialog_title": "Dryer Temp/Time Settings",
|
||||
"ace_dry_dialog_temp": "Temperature (30-80°C)",
|
||||
"ace_dry_dialog_time": "Rem. Time (h:m:s)",
|
||||
"ace_dry_dialog_confirm": "Confirm",
|
||||
"ace_dry_dialog_cancel": "Cancel",
|
||||
"ace_dry_dialog_save_restart": "Save & Restart",
|
||||
"ace_dry_dialog_custom_name": "Custom Name",
|
||||
"ace_dry_dialog_reset_default": "Reset to Default",
|
||||
"cam_placeholder": "📷 Camera not started",
|
||||
"cam_stream_unavailable": "Stream unavailable",
|
||||
"add_printer": "Add printer",
|
||||
"ams_empty": "Empty",
|
||||
"ams_no_data": "No AMS data received",
|
||||
"apd_cancel": "Cancel",
|
||||
"apd_confirm": "Add",
|
||||
"apd_err_ip": "Please enter an IP address",
|
||||
"apd_fetching": "Fetching data from printer…",
|
||||
"apd_lbl_ip": "Printer IP",
|
||||
"apd_lbl_name": "Name (optional)",
|
||||
"apd_placeholder_name": "e.g. Kobra X Living Room",
|
||||
"apd_success": "Printer added, bridge restarting…",
|
||||
"apd_title": "Add printer",
|
||||
"btn_cam_start": "▶ Camera",
|
||||
"btn_cam_start2": "▶ Start",
|
||||
"btn_cam_stop": "◼ Camera",
|
||||
"btn_cam_stop2": "◼ Stop",
|
||||
"btn_cancel": "✕ Stop",
|
||||
"btn_cancel_generic": "Cancel",
|
||||
"btn_confirm_generic": "Confirm",
|
||||
"btn_connect": "⚡ Connect",
|
||||
"btn_delete": "Delete",
|
||||
"btn_disable_motors": "Motors Off",
|
||||
"btn_disconnect": "✕ Disconnect",
|
||||
"btn_home_all": "Home All",
|
||||
"btn_home_xy": "Home XY",
|
||||
"btn_home_z": "Home Z",
|
||||
"btn_pause": "⏸ Pause",
|
||||
"btn_resume": "▶ Resume",
|
||||
"btn_cancel": "✕ Stop",
|
||||
"label_nozzle": "Nozzle",
|
||||
"cam_placeholder": "📷 Camera not started",
|
||||
"cam_stream_unavailable": "Stream unavailable",
|
||||
"card_ace_dry": "ACE Drying",
|
||||
"card_ams": "Filament",
|
||||
"card_cam": "Camera",
|
||||
"card_light_fan": "Fan",
|
||||
"card_progress": "Progress",
|
||||
"card_speed": "Print Speed",
|
||||
"card_temps": "Temperatures",
|
||||
"confirm_cancel": "Really cancel the print?",
|
||||
"fd_cancel": "Cancel",
|
||||
"fd_no_matching_material": "No matching material",
|
||||
"fd_no_slots_msg": "No loaded AMS slots.{br}Start print anyway?",
|
||||
"fd_objects_hint": "Skip objects (optional):",
|
||||
"fd_objects_toggle": "Skip objects",
|
||||
"fd_options_title": "Print Options",
|
||||
"fd_print": "▶ Print",
|
||||
"fd_slot": "Slot",
|
||||
"fd_slots_hint": "Assign GCode channel to AMS slot:",
|
||||
"fd_title": "Slot Assignment",
|
||||
"fd_used": "USED",
|
||||
"file_cancel_btn": "✕ Cancel",
|
||||
"file_ready_btn": "▶ Start Print",
|
||||
"file_slots_btn": "🎨 Select Slots",
|
||||
"header_status_complete": "Complete",
|
||||
"header_status_error": "Error",
|
||||
"header_status_printing": "Printing",
|
||||
"header_status_standby": "Ready",
|
||||
"hint_ip_no_port": "IP address only, no port (e.g. 192.168.1.102)",
|
||||
"kobra_auto_leveling": "Auto Leveling",
|
||||
"kobra_busy": "Busy",
|
||||
"kobra_canceled": "Cancelled",
|
||||
"kobra_checking": "Checking",
|
||||
"kobra_failed": "Error",
|
||||
"kobra_finished": "Finished",
|
||||
"kobra_free": "Ready",
|
||||
"kobra_init": "Initializing",
|
||||
"kobra_offline": "Offline",
|
||||
"kobra_paused": "Paused",
|
||||
"kobra_pausing": "Pausing...",
|
||||
"kobra_preheating": "Preheating",
|
||||
"kobra_printing": "Printing",
|
||||
"kobra_resumed": "Resumed",
|
||||
"kobra_resuming": "Resuming...",
|
||||
"kobra_stoped": "Stopped",
|
||||
"kobra_stopping": "Stopping...",
|
||||
"kobra_updated": "Updating",
|
||||
"label_bed": "Bed",
|
||||
"label_fan": "🌀 Fan",
|
||||
"label_light": "💡 Light",
|
||||
"label_on_off": "On / Off",
|
||||
"label_speed": "Speed",
|
||||
"panel_print_title": "Print Control",
|
||||
"panel_print_btn_pause": "⏸ Pause",
|
||||
"panel_print_btn_resume": "▶ Resume",
|
||||
"panel_print_btn_cancel": "✕ Cancel",
|
||||
"panel_print_temps_live": "Temperatures (Live)",
|
||||
"label_set": "Set",
|
||||
"label_nozzle": "Nozzle",
|
||||
"label_off": "Off",
|
||||
"panel_temps_nozzle": "Nozzle",
|
||||
"panel_temps_bed": "Heated Bed",
|
||||
"panel_temps_chart": "History (last 60 readings)",
|
||||
"label_target_c": "Target:",
|
||||
"panel_motion_xy": "XY Axes",
|
||||
"panel_motion_z": "Z Axis",
|
||||
"label_step": "Step size:",
|
||||
"btn_home_z": "Home Z",
|
||||
"btn_home_xy": "Home XY",
|
||||
"btn_home_all": "Home All",
|
||||
"btn_disable_motors": "Motors Off",
|
||||
"panel_ams_title": "Filament",
|
||||
"card_ams": "Filament",
|
||||
"ams_no_data": "No AMS data received",
|
||||
"label_on_off": "On / Off",
|
||||
"label_set": "Set",
|
||||
"label_slot": "Slot",
|
||||
"ams_empty": "Empty",
|
||||
"panel_extras_light": "Light",
|
||||
"panel_extras_fan": "Fan",
|
||||
"panel_extras_camera": "Camera",
|
||||
"btn_cam_start2": "▶ Start",
|
||||
"btn_cam_stop2": "◼ Stop",
|
||||
"panel_console_title": "Event Log",
|
||||
"log_light_on": "Light on",
|
||||
"log_light_off": "Light off",
|
||||
"log_fan": "Fan →",
|
||||
"log_nozzle": "Nozzle →",
|
||||
"log_bed": "Bed →",
|
||||
"label_speed": "Speed",
|
||||
"label_step": "Step size:",
|
||||
"label_target_c": "Target:",
|
||||
"lbl_conn_error": "Connection error:",
|
||||
"lbl_elapsed": "Elapsed:",
|
||||
"lbl_feed": "Load",
|
||||
"lbl_layers": "Layer",
|
||||
"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",
|
||||
"log_axis": "Axis",
|
||||
"log_home": "Home",
|
||||
"log_home_all": "Home All",
|
||||
"log_bed": "Bed →",
|
||||
"log_cam_start": "Camera started:",
|
||||
"log_cam_stop": "Camera stopped",
|
||||
"log_poll_error": "Poll error:",
|
||||
"log_clear": "✕ Clear",
|
||||
"log_delete_failed": "Delete failed",
|
||||
"log_dir_all": "All",
|
||||
"log_dir_label": "Dir:",
|
||||
"log_dir_rx": "RX",
|
||||
"log_dir_tx": "TX",
|
||||
"log_download": "⬇ Download",
|
||||
"log_error": "Error:",
|
||||
"confirm_cancel": "Really cancel the print?",
|
||||
"settings_title": "Settings",
|
||||
"settings_connection": "Connection",
|
||||
"settings_print": "Print Settings",
|
||||
"settings_poll": "Poll Interval",
|
||||
"settings_version": "Version",
|
||||
"settings_save": "Save & Restart",
|
||||
"settings_printer_name": "Printer Name",
|
||||
"settings_printer_ip": "Printer IP",
|
||||
"settings_mqtt_port": "MQTT Port",
|
||||
"settings_username": "MQTT Username",
|
||||
"settings_password": "MQTT Password",
|
||||
"settings_device_id": "Device ID",
|
||||
"settings_mode_id": "Mode ID",
|
||||
"hint_ip_no_port": "IP address only, no port (e.g. 192.168.1.102)",
|
||||
"settings_default_slot": "Default Slot (single color)",
|
||||
"settings_slot_auto": "Auto (all loaded slots)",
|
||||
"settings_auto_leveling": "Auto-Leveling before print",
|
||||
"settings_camera_on_print": "Turn camera on at print start",
|
||||
"settings_web_upload_warning": "Show warning when printing web uploads",
|
||||
"update_check": "Check for Updates",
|
||||
"update_checking": "Checking...",
|
||||
"update_available": "available",
|
||||
"update_none": "Already up to date",
|
||||
"update_apply": "Install Now",
|
||||
"update_applying": "Downloading...",
|
||||
"update_restarting": "Restarting...",
|
||||
"update_error": "Error",
|
||||
"btn_connect": "⚡ Connect",
|
||||
"btn_disconnect": "✕ Disconnect",
|
||||
"lbl_conn_error": "Connection error:",
|
||||
"slot_edit_title": "Edit Slot",
|
||||
"slot_edit_color": "Color",
|
||||
"slot_edit_material": "Material",
|
||||
"slot_edit_load": "⬇ Load",
|
||||
"slot_edit_unload": "⬆ Unload",
|
||||
"slot_edit_save": "💾 Save",
|
||||
"slot_edit_custom": "e.g. PLA, PETG, ABS…",
|
||||
"slot_edit_ok": "AMS Slot",
|
||||
"slot_edit_profile": "OrcaSlicer profile",
|
||||
"slot_edit_profile_hint": "Sent on OrcaSlicer sync as the specific brand instead of just \"Generic\"",
|
||||
"slot_edit_profile_default": "— Generic (default) —",
|
||||
"orca_profile_section": "OrcaSlicer Profiles",
|
||||
"log_fan": "Fan →",
|
||||
"log_filter_placeholder": "Filter…",
|
||||
"log_home": "Home",
|
||||
"log_home_all": "Home All",
|
||||
"log_light_off": "Light off",
|
||||
"log_light_on": "Light on",
|
||||
"log_lvl_err": "⛔ Errors",
|
||||
"log_lvl_label": "Level:",
|
||||
"log_lvl_warn": "⚠ Warn",
|
||||
"log_nozzle": "Nozzle →",
|
||||
"log_poll_error": "Poll error:",
|
||||
"log_print_action": "Print:",
|
||||
"log_print_start": "Print start:",
|
||||
"log_topic_ams": "AMS",
|
||||
"log_topic_info": "Info",
|
||||
"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",
|
||||
"nav_dashboard": "Dashboard",
|
||||
"nav_extras": "Light / Fan",
|
||||
"nav_motion": "Motion",
|
||||
"nav_print": "Print",
|
||||
"nav_printers": "Printers",
|
||||
"nav_settings": "Settings",
|
||||
"nav_temps": "Temperatures",
|
||||
"orca_profile_done": "Imported",
|
||||
"orca_profile_dropmsg": "Drop here or click",
|
||||
"orca_profile_help_html": "Upload a <b>ZIP</b> of your OrcaSlicer filament folder or single <b>.json</b> files.<br>In OrcaSlicer: <i>Help → Show Configuration Folder → user/<id>/filament/</i>",
|
||||
"orca_profile_hint": "Import your own OrcaSlicer filament profiles (open the user dir via Help → Show Configuration Folder)",
|
||||
"orca_profile_import_btn": "Import profiles",
|
||||
"orca_profile_import_link": "★ Import own profiles…",
|
||||
"orca_profile_import_title": "Import your OrcaSlicer profiles",
|
||||
"orca_profile_help_html": "Upload a <b>ZIP</b> of your OrcaSlicer filament folder or single <b>.json</b> files.<br>In OrcaSlicer: <i>Help → Show Configuration Folder → user/<id>/filament/</i>",
|
||||
"orca_profile_dropmsg": "Drop here or click",
|
||||
"orca_profile_list_label": "Currently imported",
|
||||
"orca_profile_user_label": "Own profiles",
|
||||
"orca_profile_user_empty": "– none –",
|
||||
"orca_profile_uploading": "Uploading…",
|
||||
"orca_profile_done": "Imported",
|
||||
"orca_profile_section": "OrcaSlicer Profiles",
|
||||
"orca_profile_skipped": "skipped",
|
||||
"log_dir_all": "All",
|
||||
"log_lvl_label": "Level:",
|
||||
"file_ready_btn": "▶ Start Print",
|
||||
"file_slots_btn": "🎨 Select Slots",
|
||||
"file_cancel_btn": "✕ Cancel",
|
||||
"nav_printers": "Printers",
|
||||
"skip_title": "✂ Skip objects",
|
||||
"skip_hint": "Uncheck objects you no longer want to print:",
|
||||
"skip_btn_label": "Objects",
|
||||
"skip_no_objects": "No objects in this print.",
|
||||
"orca_profile_uploading": "Uploading…",
|
||||
"orca_profile_user_empty": "– none –",
|
||||
"orca_profile_user_label": "Own profiles",
|
||||
"panel_ams_title": "Filament",
|
||||
"panel_browser_title": "File Browser",
|
||||
"panel_console_title": "Event Log",
|
||||
"panel_extras_camera": "Camera",
|
||||
"panel_extras_fan": "Fan",
|
||||
"panel_extras_light": "Light",
|
||||
"panel_motion_xy": "XY Axes",
|
||||
"panel_motion_z": "Z Axis",
|
||||
"panel_print_btn_cancel": "✕ Cancel",
|
||||
"panel_print_btn_pause": "⏸ Pause",
|
||||
"panel_print_btn_resume": "▶ Resume",
|
||||
"panel_print_temps_live": "Temperatures (Live)",
|
||||
"panel_print_title": "Print Control",
|
||||
"panel_temps_bed": "Heated Bed",
|
||||
"panel_temps_chart": "History (last 60 readings)",
|
||||
"panel_temps_nozzle": "Nozzle",
|
||||
"print_auto_leveling": "Auto-Leveling",
|
||||
"printers_active": "● active",
|
||||
"printers_current": "Current printer",
|
||||
"printers_empty_hint": "No printer set up yet.",
|
||||
"printers_loading": "Loading…",
|
||||
"printers_none": "No printers configured.",
|
||||
"printers_remove": "Remove printer",
|
||||
"printers_remove_confirm": "Remove printer \"{name}\"? The bridge will restart.",
|
||||
"printers_switch": "Switch →",
|
||||
"progress_action_clear": "Clear",
|
||||
"progress_action_print": "Print",
|
||||
"progress_action_slots": "Map slots",
|
||||
"settings_auto_leveling": "Auto-Leveling Default",
|
||||
"settings_auto_leveling_label": "Auto-Leveling before print",
|
||||
"settings_btn_tooltip": "Settings",
|
||||
"settings_camera_on_print": "Turn camera on at print start",
|
||||
"settings_cat_connection": "Connection",
|
||||
"settings_cat_display": "Appearance",
|
||||
"settings_cat_filament": "Filament",
|
||||
"settings_cat_language": "Language",
|
||||
"settings_cat_printer": "Printer",
|
||||
"settings_cat_system": "System",
|
||||
"settings_cat_theme": "Toggle light / dark",
|
||||
"settings_connection": "Connection",
|
||||
"settings_default_slot": "Default Slot (single color)",
|
||||
"settings_device_id": "Device ID",
|
||||
"settings_device_id_hint": "32 hex characters",
|
||||
"settings_device_id_placeholder": "32 hex characters",
|
||||
"settings_filament_mapping": "Filament profile mapping (per slot)",
|
||||
"settings_filament_mapping_hint": "Fixed Orca profile per AMS slot. On slicer sync, the bridge sends this profile instead of \"Generic\".",
|
||||
"settings_filament_mapping_label": "Filament profile mapping (per slot)",
|
||||
"settings_filament_mapping_save": "Save mapping",
|
||||
"settings_filament_mapping_save_label": "Save mapping",
|
||||
"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",
|
||||
"settings_mqtt_port": "MQTT Port",
|
||||
"settings_mqtt_username_placeholder": "userXXXXXXXX",
|
||||
"settings_orca_profiles_import": "Import profiles",
|
||||
"settings_orca_profiles_label": "OrcaSlicer Profiles",
|
||||
"settings_password": "MQTT Password",
|
||||
"settings_poll": "Poll Interval (seconds)",
|
||||
"settings_poll_interval_hint": "How often the bridge queries printer status",
|
||||
"settings_poll_interval_label": "Poll Interval (seconds)",
|
||||
"settings_print": "Print Settings",
|
||||
"settings_printer_ip": "Printer IP",
|
||||
"settings_printer_name": "Printer Name",
|
||||
"settings_printer_name_placeholder": "e.g. Kobra X Left",
|
||||
"settings_save": "Save & Restart",
|
||||
"settings_slot_auto": "Auto (all loaded slots)",
|
||||
"settings_theme_toggle": "Toggle light / dark",
|
||||
"settings_title": "Settings",
|
||||
"settings_username": "MQTT Username",
|
||||
"settings_vendor_filter_placeholder": "Search vendors…",
|
||||
"settings_version": "Version",
|
||||
"settings_visible_vendors": "Visible vendors (profile dropdown)",
|
||||
"settings_visible_vendors_hint": "Only these vendors appear in the slot profile dropdown. Nothing selected = show all. \"Generic\" and your own profiles are always visible.",
|
||||
"settings_visible_vendors_label": "Visible vendors (profile dropdown)",
|
||||
"settings_visible_vendors_save": "Save selection",
|
||||
"settings_visible_vendors_save_label": "Save selection",
|
||||
"settings_web_upload_warning": "Show warning when printing web uploads",
|
||||
"sf_all": "All",
|
||||
"sf_err": "✗ Failed",
|
||||
"sf_new": "New",
|
||||
"sf_ok": "✓ Completed",
|
||||
"skip_already": "skipped",
|
||||
"skip_btn_label": "Objects",
|
||||
"skip_cancel": "Cancel",
|
||||
"skip_confirm": "Skip",
|
||||
"skip_confirm_btn": "Skip",
|
||||
"skip_hint": "Uncheck objects you no longer want to print:",
|
||||
"skip_no_objects": "No objects in this print.",
|
||||
"skip_select_at_least_one": "Please pick at least one object.",
|
||||
"skip_sending": "Sending …",
|
||||
"skip_success": "Objects will be skipped.",
|
||||
"fd_objects_hint": "Skip objects (optional):",
|
||||
"fd_slots_hint": "Assign GCode channel to AMS slot:",
|
||||
"fd_cancel": "Cancel",
|
||||
"fd_print": "▶ Print",
|
||||
"fd_no_slots_msg": "No loaded AMS slots.{br}Start print anyway?",
|
||||
"fd_slot": "Slot",
|
||||
"fd_no_matching_material": "No matching material",
|
||||
"fd_used": "USED",
|
||||
"add_printer": "Add printer",
|
||||
"apd_lbl_ip": "Printer IP",
|
||||
"apd_lbl_name": "Name (optional)",
|
||||
"apd_placeholder_name": "e.g. Kobra X Living Room",
|
||||
"apd_cancel": "Cancel",
|
||||
"apd_confirm": "Add",
|
||||
"apd_fetching": "Fetching data from printer…",
|
||||
"apd_success": "Printer added, bridge restarting…",
|
||||
"apd_err_ip": "Please enter an IP address",
|
||||
"printers_remove": "Remove printer",
|
||||
"printers_remove_confirm": "Remove printer \"{name}\"? The bridge will restart.",
|
||||
"printers_active": "● active",
|
||||
"printers_switch": "Switch →",
|
||||
"printers_current": "Current printer",
|
||||
"printers_loading": "Loading…",
|
||||
"printers_none": "No printers configured.",
|
||||
"printers_empty_hint": "No printer set up yet.",
|
||||
"nav_browser": "Browser",
|
||||
"panel_browser_title": "File Browser",
|
||||
"store_search_placeholder": "🔍 Search…",
|
||||
"store_empty": "No files uploaded yet.",
|
||||
"store_refresh": "↻ Refresh",
|
||||
"store_print": "▶ Print",
|
||||
"store_download": "⬇ Download",
|
||||
"store_delete_confirm": "Delete file?",
|
||||
"store_print_confirm": "Print file?",
|
||||
"store_web_verify_title": "Verify file",
|
||||
"store_web_verify_msg": "Please verify this file was made for Anycubic Kobra X.",
|
||||
"store_web_verify_confirm": "Confirm",
|
||||
"store_web_verify_abort": "Abort",
|
||||
"store_no_results": "No files found.",
|
||||
"store_never": "never printed",
|
||||
"store_estimate": "Estimate",
|
||||
"store_upload_label_prefix": "Drag GCode here or ",
|
||||
"store_upload_label_browse": "browse",
|
||||
"store_upload_busy": "⏳ Uploading…",
|
||||
"store_upload_success": "✓ {file}",
|
||||
"store_upload_error": "✗ {error}",
|
||||
"sf_all": "All",
|
||||
"sf_ok": "✓ Completed",
|
||||
"sf_err": "✗ Failed",
|
||||
"sf_new": "New",
|
||||
"skip_title": "✂ Skip objects",
|
||||
"slot_edit_color": "Color",
|
||||
"slot_edit_custom": "e.g. PLA, PETG, ABS…",
|
||||
"slot_edit_load": "⬇ Load",
|
||||
"slot_edit_material": "Material",
|
||||
"slot_edit_ok": "AMS Slot",
|
||||
"slot_edit_profile": "OrcaSlicer profile",
|
||||
"slot_edit_profile_default": "— Generic (default) —",
|
||||
"slot_edit_profile_hint": "Sent on OrcaSlicer sync as the specific brand instead of just \"Generic\"",
|
||||
"slot_edit_save": "💾 Save",
|
||||
"slot_edit_title": "Edit Slot",
|
||||
"slot_edit_unload": "⬆ Unload",
|
||||
"speed_normal": "⚡ Normal",
|
||||
"speed_silent": "🐢 Silent",
|
||||
"speed_sport": "🚀 Sport",
|
||||
"ss_date": "↓ Date",
|
||||
"ss_dur": "⏱ Print time",
|
||||
"ss_name": "A–Z Name",
|
||||
"ss_dur": "⏱ Print time"
|
||||
"store_delete_confirm": "Delete file?",
|
||||
"store_download": "⬇ Download",
|
||||
"store_empty": "No files uploaded yet.",
|
||||
"store_estimate": "Estimate",
|
||||
"store_never": "never printed",
|
||||
"store_no_results": "No files found.",
|
||||
"store_print": "▶ Print",
|
||||
"store_print_confirm": "Print file?",
|
||||
"store_refresh": "↻ Refresh",
|
||||
"store_search_placeholder": "🔍 Search…",
|
||||
"store_upload_busy": "⏳ Uploading…",
|
||||
"store_upload_error": "✗ {error}",
|
||||
"store_upload_label_browse": "browse",
|
||||
"store_upload_label_prefix": "Drag GCode here or ",
|
||||
"store_upload_only_gcode": "✗ Only GCode files allowed (.gcode, .3mf, .bgcode)",
|
||||
"store_upload_success": "✓ {file}",
|
||||
"store_web_verify_abort": "Cancel",
|
||||
"store_web_verify_confirm": "Confirm",
|
||||
"store_web_verify_msg": "Please verify this file was made for Anycubic Kobra X.",
|
||||
"store_web_verify_title": "Verify file",
|
||||
"update_apply": "Install Now",
|
||||
"update_applying": "Downloading...",
|
||||
"update_available": "available",
|
||||
"update_check": "Check for Updates",
|
||||
"update_checking": "Checking...",
|
||||
"update_docker": "Copy Command",
|
||||
"update_docker_copied": "Copied! Run: docker compose pull && docker compose up -d",
|
||||
"update_error": "Error",
|
||||
"update_none": "Already up to date",
|
||||
"update_restarting": "Restarting..."
|
||||
}
|
||||
|
||||
@@ -1,247 +1,329 @@
|
||||
{
|
||||
"header_status_standby": "Listo",
|
||||
"header_status_printing": "Imprimiendo",
|
||||
"header_status_complete": "Completado",
|
||||
"header_status_error": "Error",
|
||||
"kobra_free": "Listo",
|
||||
"kobra_busy": "Ocupado",
|
||||
"kobra_printing": "Imprimiendo",
|
||||
"kobra_preheating": "Precalentando",
|
||||
"kobra_auto_leveling": "Autonivelado",
|
||||
"kobra_checking": "Comprobando",
|
||||
"kobra_updated": "Actualizando",
|
||||
"kobra_init": "Inicializando",
|
||||
"kobra_pausing": "Pausando...",
|
||||
"kobra_paused": "Pausado",
|
||||
"kobra_resuming": "Reanudando...",
|
||||
"kobra_resumed": "Reanudado",
|
||||
"kobra_stopping": "Deteniendo...",
|
||||
"kobra_stoped": "Detenido",
|
||||
"kobra_finished": "Finalizado",
|
||||
"kobra_failed": "Error",
|
||||
"kobra_canceled": "Cancelado",
|
||||
"kobra_offline": "Desconectada",
|
||||
"nav_dashboard": "Panel",
|
||||
"nav_print": "Impresión",
|
||||
"nav_temps": "Temperaturas",
|
||||
"nav_motion": "Movimiento",
|
||||
"nav_ams": "AMS",
|
||||
"nav_extras": "Luz / Ventilador",
|
||||
"nav_console": "Consola",
|
||||
"card_progress": "Progreso",
|
||||
"card_temps": "Temperaturas",
|
||||
"card_light_fan": "Ventilador",
|
||||
"card_speed": "Velocidad de impresión",
|
||||
"card_cam": "Cámara",
|
||||
"lbl_elapsed": "Transcurrido:",
|
||||
"lbl_remaining": "Restante:",
|
||||
"lbl_slicer_time": "Estimación del slicer:",
|
||||
"lbl_layers": "Capa",
|
||||
"speed_silent": "🐢 Silencioso",
|
||||
"speed_normal": "⚡ Normal",
|
||||
"speed_sport": "🚀 Sport",
|
||||
"lbl_light": "💡 Luz",
|
||||
"lbl_feed": "Cargar",
|
||||
"lbl_unload": "Descargar",
|
||||
"card_ace_dry": "Secado ACE",
|
||||
"ace_dry_auto_refill": "Relleno automático",
|
||||
"ace_dry_chart": "Historial (Temp/Humedad)",
|
||||
"ace_dry_current_temp": "Temperatura",
|
||||
"ace_dry_dialog_cancel": "Cancelar",
|
||||
"ace_dry_dialog_confirm": "Confirmar",
|
||||
"ace_dry_dialog_custom_name": "Nombre personalizado",
|
||||
"ace_dry_dialog_reset_default": "Restablecer valores predeterminados",
|
||||
"ace_dry_dialog_save_restart": "Guardar y reiniciar",
|
||||
"ace_dry_dialog_temp": "Temperatura (30-80°C)",
|
||||
"ace_dry_dialog_time": "Tiempo restante (h:m:s)",
|
||||
"ace_dry_dialog_title": "Ajustes de temp/tiempo del secador",
|
||||
"ace_dry_dryer": "Secador",
|
||||
"ace_dry_duration": "Duración (min)",
|
||||
"ace_dry_enable": "Activar secado",
|
||||
"ace_dry_humidity": "Humedad",
|
||||
"ace_dry_preset_abs_asa": "ABS / ASA",
|
||||
"ace_dry_preset_custom": "Personalizado",
|
||||
"ace_dry_preset_pa_pc": "PA / PC",
|
||||
"ace_dry_preset_petg": "PETG",
|
||||
"ace_dry_preset_pla": "PLA",
|
||||
"ace_dry_preset_pla_plus": "PLA+",
|
||||
"ace_dry_preset_tpu": "TPU",
|
||||
"ace_dry_start": "▶ Iniciar",
|
||||
"ace_dry_status_off": "Estado: Apagado",
|
||||
"ace_dry_status_on": "Estado: Activo",
|
||||
"ace_dry_status_remaining": "Restante",
|
||||
"ace_dry_humidity": "Humedad",
|
||||
"ace_dry_current_temp": "Temperatura",
|
||||
"ace_dry_chart": "Historial (Temp/Humedad)",
|
||||
"ace_dry_temp": "Temperatura (°C)",
|
||||
"ace_dry_duration": "Duración (min)",
|
||||
"ace_dry_start": "▶ Iniciar",
|
||||
"ace_dry_stop": "■ Parar",
|
||||
"ace_dry_auto_refill": "Relleno automático",
|
||||
"ace_dry_enable": "Activar secado",
|
||||
"ace_dry_temp": "Temperatura (°C)",
|
||||
"ace_dry_temp_line": "Temperatura de secado",
|
||||
"ace_dry_time_line": "Tiempo de secado",
|
||||
"ace_dry_ui_pending": "(solo UI, backend después)",
|
||||
"ace_dry_dialog_title": "Ajustes de temp/tiempo del secador",
|
||||
"ace_dry_dialog_temp": "Temperatura (30-80°C)",
|
||||
"ace_dry_dialog_time": "Tiempo restante (h:m:s)",
|
||||
"ace_dry_dialog_confirm": "Confirmar",
|
||||
"ace_dry_dialog_cancel": "Cancelar",
|
||||
"ace_dry_dialog_save_restart": "Guardar y reiniciar",
|
||||
"ace_dry_dialog_custom_name": "Nombre personalizado",
|
||||
"ace_dry_dialog_reset_default": "Restablecer valores predeterminados",
|
||||
"cam_placeholder": "📷 Cámara no iniciada",
|
||||
"cam_stream_unavailable": "Stream no disponible",
|
||||
"add_printer": "Añadir impresora",
|
||||
"ams_empty": "Vacío",
|
||||
"ams_no_data": "No se recibieron datos de AMS",
|
||||
"apd_cancel": "Cancelar",
|
||||
"apd_confirm": "Agregar",
|
||||
"apd_err_ip": "Introduce una dirección IP",
|
||||
"apd_fetching": "Obteniendo datos de la impresora…",
|
||||
"apd_lbl_ip": "IP de impresora",
|
||||
"apd_lbl_name": "Nombre (opcional)",
|
||||
"apd_placeholder_name": "p. ej. Kobra X Sala",
|
||||
"apd_success": "Impresora añadida, reiniciando bridge…",
|
||||
"apd_title": "Agregar impresora",
|
||||
"btn_cam_start": "▶ Cámara",
|
||||
"btn_cam_start2": "▶ Iniciar",
|
||||
"btn_cam_stop": "◼ Cámara",
|
||||
"btn_cam_stop2": "◼ Detener",
|
||||
"btn_cancel": "✕ Detener",
|
||||
"btn_cancel_generic": "Cancelar",
|
||||
"btn_confirm_generic": "Confirmar",
|
||||
"btn_connect": "⚡ Conectar",
|
||||
"btn_delete": "Eliminar",
|
||||
"btn_disable_motors": "Motores apagados",
|
||||
"btn_disconnect": "✕ Desconectar",
|
||||
"btn_home_all": "Home All",
|
||||
"btn_home_xy": "Home XY",
|
||||
"btn_home_z": "Home Z",
|
||||
"btn_pause": "⏸ Pausa",
|
||||
"btn_resume": "▶ Reanudar",
|
||||
"btn_cancel": "✕ Detener",
|
||||
"label_nozzle": "Boquilla",
|
||||
"cam_placeholder": "📷 Cámara no iniciada",
|
||||
"cam_stream_unavailable": "Stream no disponible",
|
||||
"card_ace_dry": "Secado ACE",
|
||||
"card_ams": "Filamento",
|
||||
"card_cam": "Cámara",
|
||||
"card_light_fan": "Ventilador",
|
||||
"card_progress": "Progreso",
|
||||
"card_speed": "Velocidad de impresión",
|
||||
"card_temps": "Temperaturas",
|
||||
"confirm_cancel": "¿Realmente cancelar la impresión?",
|
||||
"fd_cancel": "Cancelar",
|
||||
"fd_no_matching_material": "No hay material compatible",
|
||||
"fd_no_slots_msg": "No hay slots AMS cargados.{br}¿Iniciar impresión de todos modos?",
|
||||
"fd_objects_hint": "Omitir objetos (opcional):",
|
||||
"fd_objects_toggle": "Omitir objetos",
|
||||
"fd_options_title": "Opciones",
|
||||
"fd_print": "▶ Imprimir",
|
||||
"fd_slot": "Ranura",
|
||||
"fd_slots_hint": "Asignar canal GCode a la ranura AMS:",
|
||||
"fd_title": "Asignación de ranura",
|
||||
"fd_used": "USADO",
|
||||
"file_cancel_btn": "✕ Cancelar",
|
||||
"file_ready_btn": "▶ Iniciar impresión",
|
||||
"file_slots_btn": "🎨 Seleccionar ranuras",
|
||||
"header_status_complete": "Completado",
|
||||
"header_status_error": "Error",
|
||||
"header_status_printing": "Imprimiendo",
|
||||
"header_status_standby": "Listo",
|
||||
"hint_ip_no_port": "Solo dirección IP, sin puerto (p. ej. 192.168.1.102)",
|
||||
"kobra_auto_leveling": "Autonivelado",
|
||||
"kobra_busy": "Ocupado",
|
||||
"kobra_canceled": "Cancelado",
|
||||
"kobra_checking": "Comprobando",
|
||||
"kobra_failed": "Error",
|
||||
"kobra_finished": "Finalizado",
|
||||
"kobra_free": "Listo",
|
||||
"kobra_init": "Inicializando",
|
||||
"kobra_offline": "Desconectada",
|
||||
"kobra_paused": "Pausado",
|
||||
"kobra_pausing": "Pausando...",
|
||||
"kobra_preheating": "Precalentando",
|
||||
"kobra_printing": "Imprimiendo",
|
||||
"kobra_resumed": "Reanudado",
|
||||
"kobra_resuming": "Reanudando...",
|
||||
"kobra_stoped": "Detenido",
|
||||
"kobra_stopping": "Deteniendo...",
|
||||
"kobra_updated": "Actualizando",
|
||||
"label_bed": "Cama",
|
||||
"label_fan": "🌀 Ventilador",
|
||||
"label_light": "💡 Luz",
|
||||
"label_on_off": "Encendido / Apagado",
|
||||
"label_speed": "Velocidad",
|
||||
"panel_print_title": "Control de impresión",
|
||||
"panel_print_btn_pause": "⏸ Pausa",
|
||||
"panel_print_btn_resume": "▶ Reanudar",
|
||||
"panel_print_btn_cancel": "✕ Cancelar",
|
||||
"panel_print_temps_live": "Temperaturas (en vivo)",
|
||||
"label_set": "Set",
|
||||
"label_nozzle": "Boquilla",
|
||||
"label_off": "Apagado",
|
||||
"panel_temps_nozzle": "Boquilla",
|
||||
"panel_temps_bed": "Cama caliente",
|
||||
"panel_temps_chart": "Historial (últimas 60 lecturas)",
|
||||
"label_target_c": "Objetivo:",
|
||||
"panel_motion_xy": "Ejes XY",
|
||||
"panel_motion_z": "Eje Z",
|
||||
"label_step": "Tamaño del paso:",
|
||||
"btn_home_z": "Home Z",
|
||||
"btn_home_xy": "Home XY",
|
||||
"btn_home_all": "Home All",
|
||||
"btn_disable_motors": "Motores apagados",
|
||||
"panel_ams_title": "Filamento",
|
||||
"card_ams": "Filamento",
|
||||
"ams_no_data": "No se recibieron datos de AMS",
|
||||
"label_on_off": "Encendido / Apagado",
|
||||
"label_set": "Set",
|
||||
"label_slot": "Ranura",
|
||||
"ams_empty": "Vacío",
|
||||
"panel_extras_light": "Luz",
|
||||
"panel_extras_fan": "Ventilador",
|
||||
"panel_extras_camera": "Cámara",
|
||||
"btn_cam_start2": "▶ Iniciar",
|
||||
"btn_cam_stop2": "◼ Detener",
|
||||
"panel_console_title": "Registro de eventos",
|
||||
"log_light_on": "Luz encendida",
|
||||
"log_light_off": "Luz apagada",
|
||||
"log_fan": "Ventilador →",
|
||||
"log_nozzle": "Boquilla →",
|
||||
"log_bed": "Cama →",
|
||||
"label_speed": "Velocidad",
|
||||
"label_step": "Tamaño del paso:",
|
||||
"label_target_c": "Objetivo:",
|
||||
"lbl_conn_error": "Error de conexión:",
|
||||
"lbl_elapsed": "Transcurrido:",
|
||||
"lbl_feed": "Cargar",
|
||||
"lbl_layers": "Capa",
|
||||
"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",
|
||||
"log_axis": "Eje",
|
||||
"log_home": "Home",
|
||||
"log_home_all": "Home All",
|
||||
"log_bed": "Cama →",
|
||||
"log_cam_start": "Cámara iniciada:",
|
||||
"log_cam_stop": "Cámara detenida",
|
||||
"log_poll_error": "Error de sondeo:",
|
||||
"log_clear": "✕ Limpiar",
|
||||
"log_delete_failed": "Error al eliminar",
|
||||
"log_dir_all": "Todos",
|
||||
"log_dir_label": "Dirección:",
|
||||
"log_dir_rx": "RX",
|
||||
"log_dir_tx": "TX",
|
||||
"log_download": "⬇ Descargar",
|
||||
"log_error": "Error:",
|
||||
"confirm_cancel": "¿Realmente cancelar la impresión?",
|
||||
"settings_title": "Configuración",
|
||||
"settings_connection": "Conexión",
|
||||
"settings_print": "Ajustes de impresión",
|
||||
"settings_poll": "Intervalo de sondeo",
|
||||
"settings_version": "Versión",
|
||||
"settings_save": "Guardar y reiniciar",
|
||||
"settings_printer_name": "Nombre de impresora",
|
||||
"settings_printer_ip": "IP de impresora",
|
||||
"settings_mqtt_port": "MQTT Port",
|
||||
"settings_username": "Usuario MQTT",
|
||||
"settings_password": "Contraseña MQTT",
|
||||
"settings_device_id": "ID del dispositivo",
|
||||
"settings_mode_id": "ID de modo",
|
||||
"hint_ip_no_port": "Solo dirección IP, sin puerto (p. ej. 192.168.1.102)",
|
||||
"settings_default_slot": "Ranura predeterminada (un color)",
|
||||
"settings_slot_auto": "Auto (todos los slots cargados)",
|
||||
"settings_auto_leveling": "Autonivelado antes de imprimir",
|
||||
"settings_camera_on_print": "Encender cámara al iniciar impresión",
|
||||
"settings_web_upload_warning": "Mostrar advertencia al imprimir subidas web",
|
||||
"update_check": "Buscar actualizaciones",
|
||||
"update_checking": "Comprobando...",
|
||||
"update_available": "disponible",
|
||||
"update_none": "Ya actualizado",
|
||||
"update_apply": "Instalar ahora",
|
||||
"update_applying": "Descargando...",
|
||||
"update_restarting": "Reiniciando...",
|
||||
"update_error": "Error",
|
||||
"btn_connect": "⚡ Conectar",
|
||||
"btn_disconnect": "✕ Desconectar",
|
||||
"lbl_conn_error": "Error de conexión:",
|
||||
"slot_edit_title": "Editar slot",
|
||||
"slot_edit_color": "Color",
|
||||
"slot_edit_material": "Material",
|
||||
"slot_edit_load": "⬇ Cargar",
|
||||
"slot_edit_unload": "⬆ Descargar",
|
||||
"slot_edit_save": "💾 Guardar",
|
||||
"slot_edit_custom": "p. ej. PLA, PETG, ABS…",
|
||||
"slot_edit_ok": "Ranura AMS",
|
||||
"slot_edit_profile": "Perfil de OrcaSlicer",
|
||||
"slot_edit_profile_hint": "Envía al sincronizar con OrcaSlicer la marca concreta en lugar de solo \"Generic\"",
|
||||
"slot_edit_profile_default": "— Genérico (Predeterminado) —",
|
||||
"orca_profile_section": "Perfiles de OrcaSlicer",
|
||||
"log_fan": "Ventilador →",
|
||||
"log_filter_placeholder": "Filtrar…",
|
||||
"log_home": "Home",
|
||||
"log_home_all": "Home All",
|
||||
"log_light_off": "Luz apagada",
|
||||
"log_light_on": "Luz encendida",
|
||||
"log_lvl_err": "⛔ Errores",
|
||||
"log_lvl_label": "Nivel:",
|
||||
"log_lvl_warn": "⚠ Avisos",
|
||||
"log_nozzle": "Boquilla →",
|
||||
"log_poll_error": "Error de sondeo:",
|
||||
"log_print_action": "Impresión:",
|
||||
"log_print_start": "Inicio de impresión:",
|
||||
"log_topic_ams": "AMS",
|
||||
"log_topic_info": "Info",
|
||||
"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",
|
||||
"nav_dashboard": "Panel",
|
||||
"nav_extras": "Luz / Ventilador",
|
||||
"nav_motion": "Movimiento",
|
||||
"nav_print": "Impresión",
|
||||
"nav_printers": "Impresoras",
|
||||
"nav_settings": "Ajustes",
|
||||
"nav_temps": "Temperaturas",
|
||||
"orca_profile_done": "Importado",
|
||||
"orca_profile_dropmsg": "Suelta aquí o haz clic",
|
||||
"orca_profile_help_html": "Sube un <b>ZIP</b> de tu carpeta de filamentos de OrcaSlicer o archivos <b>.json</b> sueltos.<br>En OrcaSlicer: <i>Help → Show Configuration Folder → user/<id>/filament/</i>",
|
||||
"orca_profile_hint": "Importa tus propios perfiles de filamento de OrcaSlicer (abre el directorio del usuario vía Help → Show Configuration Folder)",
|
||||
"orca_profile_import_btn": "Importar perfiles",
|
||||
"orca_profile_import_link": "★ Importar perfiles propios…",
|
||||
"orca_profile_import_title": "Importar tus perfiles de OrcaSlicer",
|
||||
"orca_profile_help_html": "Sube un <b>ZIP</b> de tu carpeta de filamentos de OrcaSlicer o archivos <b>.json</b> sueltos.<br>En OrcaSlicer: <i>Help → Show Configuration Folder → user/<id>/filament/</i>",
|
||||
"orca_profile_dropmsg": "Suelta aquí o haz clic",
|
||||
"orca_profile_list_label": "Actualmente importados",
|
||||
"orca_profile_user_label": "Perfiles propios",
|
||||
"orca_profile_user_empty": "– ninguno –",
|
||||
"orca_profile_uploading": "Subiendo…",
|
||||
"orca_profile_done": "Importado",
|
||||
"orca_profile_section": "Perfiles de OrcaSlicer",
|
||||
"orca_profile_skipped": "omitido",
|
||||
"log_dir_all": "Todos",
|
||||
"log_lvl_label": "Nivel:",
|
||||
"file_ready_btn": "▶ Iniciar impresión",
|
||||
"file_slots_btn": "🎨 Seleccionar ranuras",
|
||||
"file_cancel_btn": "✕ Cancelar",
|
||||
"nav_printers": "Impresoras",
|
||||
"skip_title": "✂ Omitir objetos",
|
||||
"skip_hint": "Deselecciona los objetos que ya no quieras imprimir:",
|
||||
"skip_btn_label": "Objetos",
|
||||
"skip_no_objects": "No hay objetos en esta impresión.",
|
||||
"orca_profile_uploading": "Subiendo…",
|
||||
"orca_profile_user_empty": "– ninguno –",
|
||||
"orca_profile_user_label": "Perfiles propios",
|
||||
"panel_ams_title": "Filamento",
|
||||
"panel_browser_title": "Explorador de archivos",
|
||||
"panel_console_title": "Registro de eventos",
|
||||
"panel_extras_camera": "Cámara",
|
||||
"panel_extras_fan": "Ventilador",
|
||||
"panel_extras_light": "Luz",
|
||||
"panel_motion_xy": "Ejes XY",
|
||||
"panel_motion_z": "Eje Z",
|
||||
"panel_print_btn_cancel": "✕ Cancelar",
|
||||
"panel_print_btn_pause": "⏸ Pausa",
|
||||
"panel_print_btn_resume": "▶ Reanudar",
|
||||
"panel_print_temps_live": "Temperaturas (en vivo)",
|
||||
"panel_print_title": "Control de impresión",
|
||||
"panel_temps_bed": "Cama caliente",
|
||||
"panel_temps_chart": "Historial (últimas 60 lecturas)",
|
||||
"panel_temps_nozzle": "Boquilla",
|
||||
"print_auto_leveling": "Autonivelado para esta impresión",
|
||||
"printers_active": "● activa",
|
||||
"printers_current": "Impresora actual",
|
||||
"printers_empty_hint": "Aún no hay impresora configurada.",
|
||||
"printers_loading": "Cargando…",
|
||||
"printers_none": "No hay impresoras configuradas.",
|
||||
"printers_remove": "Eliminar impresora",
|
||||
"printers_remove_confirm": "¿Eliminar impresora \"{name}\"? El bridge se reiniciará.",
|
||||
"printers_switch": "Cambiar →",
|
||||
"progress_action_clear": "Vaciar",
|
||||
"progress_action_print": "Imprimir",
|
||||
"progress_action_slots": "Asignar ranuras",
|
||||
"settings_auto_leveling": "Autonivelado antes de imprimir",
|
||||
"settings_auto_leveling_label": "Autonivelado antes de imprimir",
|
||||
"settings_btn_tooltip": "Ajustes",
|
||||
"settings_camera_on_print": "Encender cámara al iniciar impresión",
|
||||
"settings_cat_connection": "Conexión",
|
||||
"settings_cat_display": "Apariencia",
|
||||
"settings_cat_filament": "Filamento",
|
||||
"settings_cat_language": "Idioma",
|
||||
"settings_cat_printer": "Impresora",
|
||||
"settings_cat_system": "Sistema",
|
||||
"settings_cat_theme": "Alternar claro / oscuro",
|
||||
"settings_connection": "Conexión",
|
||||
"settings_default_slot": "Ranura predeterminada (un color)",
|
||||
"settings_device_id": "ID del dispositivo",
|
||||
"settings_device_id_hint": "32 caracteres hexadecimales",
|
||||
"settings_device_id_placeholder": "32 caracteres hexadecimales",
|
||||
"settings_filament_mapping": "Asignación de perfil de filamento (por ranura)",
|
||||
"settings_filament_mapping_hint": "Perfil Orca fijo por ranura AMS. Al sincronizar con el slicer, el bridge envía este perfil en lugar de \"Generic\".",
|
||||
"settings_filament_mapping_label": "Asignación de perfil de filamento (por ranura)",
|
||||
"settings_filament_mapping_save": "Guardar asignación",
|
||||
"settings_filament_mapping_save_label": "Guardar asignación",
|
||||
"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",
|
||||
"settings_mqtt_port": "MQTT Port",
|
||||
"settings_mqtt_username_placeholder": "userXXXXXXXX",
|
||||
"settings_orca_profiles_import": "Importar perfiles",
|
||||
"settings_orca_profiles_label": "Perfiles de OrcaSlicer",
|
||||
"settings_password": "Contraseña MQTT",
|
||||
"settings_poll": "Intervalo de sondeo (segundos)",
|
||||
"settings_poll_interval_hint": "Con qué frecuencia el bridge consulta el estado de la impresora",
|
||||
"settings_poll_interval_label": "Intervalo de sondeo (segundos)",
|
||||
"settings_print": "Ajustes de impresión",
|
||||
"settings_printer_ip": "IP de impresora",
|
||||
"settings_printer_name": "Nombre de impresora",
|
||||
"settings_printer_name_placeholder": "p. ej. Kobra X Sala",
|
||||
"settings_save": "Guardar y reiniciar",
|
||||
"settings_slot_auto": "Auto (todos los slots cargados)",
|
||||
"settings_theme_toggle": "Alternar claro / oscuro",
|
||||
"settings_title": "Configuración",
|
||||
"settings_username": "Usuario MQTT",
|
||||
"settings_vendor_filter_placeholder": "Buscar fabricantes…",
|
||||
"settings_version": "Versión",
|
||||
"settings_visible_vendors": "Fabricantes visibles (lista de perfiles)",
|
||||
"settings_visible_vendors_hint": "Solo estos fabricantes aparecen en la lista de perfiles de ranura. Nada seleccionado = mostrar todos. «Generic» y tus propios perfiles siempre son visibles.",
|
||||
"settings_visible_vendors_label": "Fabricantes visibles (lista de perfiles)",
|
||||
"settings_visible_vendors_save": "Guardar selección",
|
||||
"settings_visible_vendors_save_label": "Guardar selección",
|
||||
"settings_web_upload_warning": "Mostrar advertencia al imprimir subidas web",
|
||||
"sf_all": "Todos",
|
||||
"sf_err": "✗ Fallido",
|
||||
"sf_new": "Nuevo",
|
||||
"sf_ok": "✓ Completado",
|
||||
"skip_already": "omitido",
|
||||
"skip_btn_label": "Objetos",
|
||||
"skip_cancel": "Cancelar",
|
||||
"skip_confirm": "Omitir",
|
||||
"skip_confirm_btn": "Omitir",
|
||||
"skip_hint": "Deselecciona los objetos que ya no quieras imprimir:",
|
||||
"skip_no_objects": "No hay objetos en esta impresión.",
|
||||
"skip_select_at_least_one": "Selecciona al menos un objeto.",
|
||||
"skip_sending": "Enviando …",
|
||||
"skip_success": "Se omitirán los objetos.",
|
||||
"fd_objects_hint": "Omitir objetos (opcional):",
|
||||
"fd_slots_hint": "Asignar canal GCode a la ranura AMS:",
|
||||
"fd_cancel": "Cancelar",
|
||||
"fd_print": "▶ Imprimir",
|
||||
"fd_no_slots_msg": "No hay slots AMS cargados.{br}¿Iniciar impresión de todos modos?",
|
||||
"fd_slot": "Ranura",
|
||||
"fd_no_matching_material": "No hay material compatible",
|
||||
"fd_used": "USADO",
|
||||
"add_printer": "Añadir impresora",
|
||||
"apd_lbl_ip": "IP de impresora",
|
||||
"apd_lbl_name": "Nombre (opcional)",
|
||||
"apd_placeholder_name": "p. ej. Kobra X Sala",
|
||||
"apd_cancel": "Cancelar",
|
||||
"apd_confirm": "Añadir",
|
||||
"apd_fetching": "Obteniendo datos de la impresora…",
|
||||
"apd_success": "Impresora añadida, reiniciando bridge…",
|
||||
"apd_err_ip": "Introduce una dirección IP",
|
||||
"printers_remove": "Eliminar impresora",
|
||||
"printers_remove_confirm": "¿Eliminar impresora \"{name}\"? El bridge se reiniciará.",
|
||||
"printers_active": "● activa",
|
||||
"printers_switch": "Cambiar →",
|
||||
"printers_current": "Impresora actual",
|
||||
"printers_loading": "Cargando…",
|
||||
"printers_none": "No hay impresoras configuradas.",
|
||||
"printers_empty_hint": "Aún no hay impresora configurada.",
|
||||
"nav_browser": "Explorador",
|
||||
"panel_browser_title": "Explorador de archivos",
|
||||
"store_search_placeholder": "🔍 Buscar…",
|
||||
"store_empty": "Aún no hay archivos subidos.",
|
||||
"store_refresh": "↻ Actualizar",
|
||||
"store_print": "▶ Imprimir",
|
||||
"store_download": "⬇ Descargar",
|
||||
"store_delete_confirm": "¿Eliminar archivo?",
|
||||
"store_print_confirm": "¿Imprimir archivo?",
|
||||
"store_web_verify_title": "Verificar archivo",
|
||||
"store_web_verify_msg": "Verifica que este archivo fue creado para Anycubic Kobra X.",
|
||||
"store_web_verify_confirm": "Confirmar",
|
||||
"store_web_verify_abort": "Abortar",
|
||||
"store_no_results": "No se encontraron archivos.",
|
||||
"store_never": "nunca impreso",
|
||||
"store_estimate": "Estimación",
|
||||
"store_upload_label_prefix": "Arrastra el GCode aquí o ",
|
||||
"store_upload_label_browse": "buscar",
|
||||
"store_upload_busy": "⏳ Subiendo…",
|
||||
"store_upload_success": "✓ {file}",
|
||||
"store_upload_error": "✗ {error}",
|
||||
"sf_all": "Todos",
|
||||
"sf_ok": "✓ Completado",
|
||||
"sf_err": "✗ Fallido",
|
||||
"sf_new": "Nuevo",
|
||||
"skip_title": "✂ Omitir objetos",
|
||||
"slot_edit_color": "Color",
|
||||
"slot_edit_custom": "p. ej. PLA, PETG, ABS…",
|
||||
"slot_edit_load": "⬇ Cargar",
|
||||
"slot_edit_material": "Material",
|
||||
"slot_edit_ok": "Ranura AMS",
|
||||
"slot_edit_profile": "Perfil de OrcaSlicer",
|
||||
"slot_edit_profile_default": "— Genérico (Predeterminado) —",
|
||||
"slot_edit_profile_hint": "Envía al sincronizar con OrcaSlicer la marca concreta en lugar de solo \"Generic\"",
|
||||
"slot_edit_save": "💾 Guardar",
|
||||
"slot_edit_title": "Editar slot",
|
||||
"slot_edit_unload": "⬆ Descargar",
|
||||
"speed_normal": "⚡ Normal",
|
||||
"speed_silent": "🐢 Silencioso",
|
||||
"speed_sport": "🚀 Sport",
|
||||
"ss_date": "↓ Fecha",
|
||||
"ss_dur": "⏱ Tiempo de impresión",
|
||||
"ss_name": "A–Z Nombre",
|
||||
"ss_dur": "⏱ Tiempo de impresión"
|
||||
"store_delete_confirm": "¿Eliminar archivo?",
|
||||
"store_download": "⬇ Descargar",
|
||||
"store_empty": "Aún no hay archivos subidos.",
|
||||
"store_estimate": "Estimación",
|
||||
"store_never": "nunca impreso",
|
||||
"store_no_results": "No se encontraron archivos.",
|
||||
"store_print": "▶ Imprimir",
|
||||
"store_print_confirm": "¿Imprimir archivo?",
|
||||
"store_refresh": "↻ Actualizar",
|
||||
"store_search_placeholder": "🔍 Buscar…",
|
||||
"store_upload_busy": "⏳ Subiendo…",
|
||||
"store_upload_error": "✗ {error}",
|
||||
"store_upload_label_browse": "buscar",
|
||||
"store_upload_label_prefix": "Arrastra el GCode aquí o ",
|
||||
"store_upload_only_gcode": "✗ Solo se permiten archivos GCode (.gcode, .3mf, .bgcode)",
|
||||
"store_upload_success": "✓ {file}",
|
||||
"store_web_verify_abort": "Cancelar",
|
||||
"store_web_verify_confirm": "Confirmar",
|
||||
"store_web_verify_msg": "Verifica que este archivo fue hecho para Anycubic Kobra X.",
|
||||
"store_web_verify_title": "Verificar archivo",
|
||||
"update_apply": "Instalar ahora",
|
||||
"update_applying": "Descargando...",
|
||||
"update_available": "disponible",
|
||||
"update_check": "Buscar actualizaciones",
|
||||
"update_checking": "Comprobando...",
|
||||
"update_docker": "Copiar comando",
|
||||
"update_docker_copied": "Copiado. Ejecutar: docker compose pull && docker compose up -d",
|
||||
"update_error": "Error",
|
||||
"update_none": "Ya actualizado",
|
||||
"update_restarting": "Reiniciando..."
|
||||
}
|
||||
|
||||
329
web/translations/fr.json
Normal file
329
web/translations/fr.json
Normal file
@@ -0,0 +1,329 @@
|
||||
{
|
||||
"ace_dry_auto_refill": "Remplissage auto",
|
||||
"ace_dry_chart": "Historique (Temp/Humidité)",
|
||||
"ace_dry_current_temp": "Température",
|
||||
"ace_dry_dialog_cancel": "Annuler",
|
||||
"ace_dry_dialog_confirm": "Confirmer",
|
||||
"ace_dry_dialog_custom_name": "Nom personnalisé",
|
||||
"ace_dry_dialog_reset_default": "Réinitialiser",
|
||||
"ace_dry_dialog_save_restart": "Enregistrer et redémarrer",
|
||||
"ace_dry_dialog_temp": "Température (30-80°C)",
|
||||
"ace_dry_dialog_time": "Temps restant (h:m:s)",
|
||||
"ace_dry_dialog_title": "Réglages Temp/Durée du séchoir",
|
||||
"ace_dry_dryer": "Séchoir",
|
||||
"ace_dry_duration": "Durée (min)",
|
||||
"ace_dry_enable": "Activer le séchage",
|
||||
"ace_dry_humidity": "Humidité",
|
||||
"ace_dry_preset_abs_asa": "ABS / ASA",
|
||||
"ace_dry_preset_custom": "Personnalisé",
|
||||
"ace_dry_preset_pa_pc": "PA / PC",
|
||||
"ace_dry_preset_petg": "PETG",
|
||||
"ace_dry_preset_pla": "PLA",
|
||||
"ace_dry_preset_pla_plus": "PLA+",
|
||||
"ace_dry_preset_tpu": "TPU",
|
||||
"ace_dry_start": "▶ Démarrer",
|
||||
"ace_dry_status_off": "Statut : Arrêté",
|
||||
"ace_dry_status_on": "Statut : Actif",
|
||||
"ace_dry_status_remaining": "Restant",
|
||||
"ace_dry_stop": "■ Arrêter",
|
||||
"ace_dry_temp": "Température (°C)",
|
||||
"ace_dry_temp_line": "Température de séchage",
|
||||
"ace_dry_time_line": "Durée de séchage",
|
||||
"ace_dry_ui_pending": "(Interface seule, backend suivant)",
|
||||
"add_printer": "Ajouter une imprimante",
|
||||
"ams_empty": "Vide",
|
||||
"ams_no_data": "Aucune donnée AMS reçue",
|
||||
"apd_cancel": "Annuler",
|
||||
"apd_confirm": "Ajouter",
|
||||
"apd_err_ip": "Veuillez saisir une adresse IP",
|
||||
"apd_fetching": "Récupération des données de l'imprimante…",
|
||||
"apd_lbl_ip": "IP de l'imprimante",
|
||||
"apd_lbl_name": "Nom (optionnel)",
|
||||
"apd_placeholder_name": "ex. Kobra X Salon",
|
||||
"apd_success": "Imprimante ajoutée, redémarrage du bridge…",
|
||||
"apd_title": "Ajouter une imprimante",
|
||||
"btn_cam_start": "▶ Caméra",
|
||||
"btn_cam_start2": "▶ Démarrer",
|
||||
"btn_cam_stop": "◼ Caméra",
|
||||
"btn_cam_stop2": "◼ Arrêter",
|
||||
"btn_cancel": "✕ Arrêter",
|
||||
"btn_cancel_generic": "Annuler",
|
||||
"btn_confirm_generic": "Confirmer",
|
||||
"btn_connect": "⚡ Connecter",
|
||||
"btn_delete": "Supprimer",
|
||||
"btn_disable_motors": "Moteurs Off",
|
||||
"btn_disconnect": "✕ Déconnecter",
|
||||
"btn_home_all": "Origine Tout",
|
||||
"btn_home_xy": "Origine XY",
|
||||
"btn_home_z": "Origine Z",
|
||||
"btn_pause": "⏸ Pause",
|
||||
"btn_resume": "▶ Reprendre",
|
||||
"cam_placeholder": "📷 Caméra non démarrée",
|
||||
"cam_stream_unavailable": "Flux indisponible",
|
||||
"card_ace_dry": "Séchage ACE",
|
||||
"card_ams": "Filament",
|
||||
"card_cam": "Caméra",
|
||||
"card_light_fan": "Ventilateur",
|
||||
"card_progress": "Progression",
|
||||
"card_speed": "Vitesse d'impression",
|
||||
"card_temps": "Températures",
|
||||
"confirm_cancel": "Vraiment annuler l'impression ?",
|
||||
"fd_cancel": "Annuler",
|
||||
"fd_no_matching_material": "Aucun matériau correspondant",
|
||||
"fd_no_slots_msg": "Aucun slot AMS chargé.{br}Lancer l'impression quand même ?",
|
||||
"fd_objects_hint": "Ignorer des objets (optionnel) :",
|
||||
"fd_objects_toggle": "Ignorer des objets",
|
||||
"fd_options_title": "Options",
|
||||
"fd_print": "▶ Imprimer",
|
||||
"fd_slot": "Slot",
|
||||
"fd_slots_hint": "Associer le canal GCode au slot AMS :",
|
||||
"fd_title": "Attribution de fente",
|
||||
"fd_used": "UTILISÉ",
|
||||
"file_cancel_btn": "✕ Annuler",
|
||||
"file_ready_btn": "▶ Lancer l'impression",
|
||||
"file_slots_btn": "🎨 Choisir les slots",
|
||||
"header_status_complete": "Terminé",
|
||||
"header_status_error": "Erreur",
|
||||
"header_status_printing": "Impression",
|
||||
"header_status_standby": "Prêt",
|
||||
"hint_ip_no_port": "Adresse IP uniquement, sans port (ex. 192.168.1.102)",
|
||||
"kobra_auto_leveling": "Mise à niveau auto",
|
||||
"kobra_busy": "Occupé",
|
||||
"kobra_canceled": "Annulé",
|
||||
"kobra_checking": "Vérification",
|
||||
"kobra_failed": "Erreur",
|
||||
"kobra_finished": "Terminé",
|
||||
"kobra_free": "Disponible",
|
||||
"kobra_init": "Initialisation",
|
||||
"kobra_offline": "Hors ligne",
|
||||
"kobra_paused": "En pause",
|
||||
"kobra_pausing": "Pause en cours…",
|
||||
"kobra_preheating": "Préchauffage",
|
||||
"kobra_printing": "Impression",
|
||||
"kobra_resumed": "Repris",
|
||||
"kobra_resuming": "Reprise en cours…",
|
||||
"kobra_stoped": "Arrêté",
|
||||
"kobra_stopping": "Arrêt en cours…",
|
||||
"kobra_updated": "Mise à jour",
|
||||
"label_bed": "Plateau",
|
||||
"label_fan": "🌀 Ventilateur",
|
||||
"label_light": "💡 Lumière",
|
||||
"label_nozzle": "Buse",
|
||||
"label_off": "Éteint",
|
||||
"label_on_off": "On / Off",
|
||||
"label_set": "Définir",
|
||||
"label_slot": "Slot",
|
||||
"label_speed": "Vitesse",
|
||||
"label_step": "Pas :",
|
||||
"label_target_c": "Cible :",
|
||||
"lbl_conn_error": "Erreur de connexion :",
|
||||
"lbl_elapsed": "Écoulé :",
|
||||
"lbl_feed": "Charger",
|
||||
"lbl_layers": "Couche",
|
||||
"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",
|
||||
"log_axis": "Axe",
|
||||
"log_bed": "Plateau →",
|
||||
"log_cam_start": "Caméra démarrée :",
|
||||
"log_cam_stop": "Caméra arrêtée",
|
||||
"log_clear": "✕ Effacer",
|
||||
"log_delete_failed": "Échec de la suppression",
|
||||
"log_dir_all": "Tout",
|
||||
"log_dir_label": "Sens :",
|
||||
"log_dir_rx": "RX",
|
||||
"log_dir_tx": "TX",
|
||||
"log_download": "⬇ Télécharger",
|
||||
"log_error": "Erreur :",
|
||||
"log_fan": "Ventilateur →",
|
||||
"log_filter_placeholder": "Filtrer…",
|
||||
"log_home": "Origine",
|
||||
"log_home_all": "Origine Tout",
|
||||
"log_light_off": "Lumière éteinte",
|
||||
"log_light_on": "Lumière allumée",
|
||||
"log_lvl_err": "⛔ Erreurs",
|
||||
"log_lvl_label": "Niveau :",
|
||||
"log_lvl_warn": "⚠ Avert.",
|
||||
"log_nozzle": "Buse →",
|
||||
"log_poll_error": "Erreur de sondage :",
|
||||
"log_print_action": "Impression :",
|
||||
"log_print_start": "Début de l'impression :",
|
||||
"log_topic_ams": "AMS",
|
||||
"log_topic_info": "Info",
|
||||
"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",
|
||||
"nav_dashboard": "Tableau de bord",
|
||||
"nav_extras": "Lumière / Ventilateur",
|
||||
"nav_motion": "Mouvement",
|
||||
"nav_print": "Impression",
|
||||
"nav_printers": "Imprimantes",
|
||||
"nav_settings": "Paramètres",
|
||||
"nav_temps": "Températures",
|
||||
"orca_profile_done": "Importé",
|
||||
"orca_profile_dropmsg": "Déposez ici ou cliquez",
|
||||
"orca_profile_help_html": "Déposez un <b>ZIP</b> de votre dossier filament OrcaSlicer ou des fichiers <b>.json</b> individuels.<br>Dans OrcaSlicer : <i>Aide → Afficher le dossier de configuration → user/<id>/filament/</i>",
|
||||
"orca_profile_hint": "Importez vos propres profils de filament OrcaSlicer (ouvrez le dossier utilisateur via Aide → Afficher le dossier de configuration)",
|
||||
"orca_profile_import_btn": "Importer des profils",
|
||||
"orca_profile_import_link": "★ Importer mes profils…",
|
||||
"orca_profile_import_title": "Importer vos profils OrcaSlicer",
|
||||
"orca_profile_list_label": "Profils importés",
|
||||
"orca_profile_section": "Profils OrcaSlicer",
|
||||
"orca_profile_skipped": "ignoré",
|
||||
"orca_profile_uploading": "Envoi en cours…",
|
||||
"orca_profile_user_empty": "– aucun –",
|
||||
"orca_profile_user_label": "Mes profils",
|
||||
"panel_ams_title": "Filament",
|
||||
"panel_browser_title": "Explorateur de fichiers",
|
||||
"panel_console_title": "Journal d'événements",
|
||||
"panel_extras_camera": "Caméra",
|
||||
"panel_extras_fan": "Ventilateur",
|
||||
"panel_extras_light": "Lumière",
|
||||
"panel_motion_xy": "Axes XY",
|
||||
"panel_motion_z": "Axe Z",
|
||||
"panel_print_btn_cancel": "✕ Annuler",
|
||||
"panel_print_btn_pause": "⏸ Pause",
|
||||
"panel_print_btn_resume": "▶ Reprendre",
|
||||
"panel_print_temps_live": "Températures (en direct)",
|
||||
"panel_print_title": "Contrôle impression",
|
||||
"panel_temps_bed": "Plateau chauffant",
|
||||
"panel_temps_chart": "Historique (60 dernières valeurs)",
|
||||
"panel_temps_nozzle": "Buse",
|
||||
"print_auto_leveling": "Mise à niveau auto pour cette impression",
|
||||
"printers_active": "● actif",
|
||||
"printers_current": "Imprimante actuelle",
|
||||
"printers_empty_hint": "Aucune imprimante configurée.",
|
||||
"printers_loading": "Chargement…",
|
||||
"printers_none": "Aucune imprimante configurée.",
|
||||
"printers_remove": "Supprimer l'imprimante",
|
||||
"printers_remove_confirm": "Supprimer l'imprimante \"{name}\" ? Le bridge va redémarrer.",
|
||||
"printers_switch": "Changer →",
|
||||
"progress_action_clear": "Vider",
|
||||
"progress_action_print": "Imprimer",
|
||||
"progress_action_slots": "Affecter les emplacements",
|
||||
"settings_auto_leveling": "Mise à niveau auto avant impression",
|
||||
"settings_auto_leveling_label": "Mise à niveau auto avant impression",
|
||||
"settings_btn_tooltip": "Paramètres",
|
||||
"settings_camera_on_print": "Activer la caméra au démarrage de l'impression",
|
||||
"settings_cat_connection": "Connexion",
|
||||
"settings_cat_display": "Apparence",
|
||||
"settings_cat_filament": "Filament",
|
||||
"settings_cat_language": "Langue",
|
||||
"settings_cat_printer": "Imprimante",
|
||||
"settings_cat_system": "Système",
|
||||
"settings_cat_theme": "Basculer clair / sombre",
|
||||
"settings_connection": "Connexion",
|
||||
"settings_default_slot": "Slot par défaut (couleur unique)",
|
||||
"settings_device_id": "ID de l'appareil",
|
||||
"settings_device_id_hint": "32 caractères hexadécimaux",
|
||||
"settings_device_id_placeholder": "32 caractères hexadécimaux",
|
||||
"settings_filament_mapping": "Mappage du profil de filament (par emplacement)",
|
||||
"settings_filament_mapping_hint": "Profil Orca fixe par emplacement AMS. Lors de la synchronisation du slicer, le bridge envoie ce profil au lieu de « Generic ».",
|
||||
"settings_filament_mapping_label": "Mappage du profil de filament (par emplacement)",
|
||||
"settings_filament_mapping_save": "Enregistrer le mappage",
|
||||
"settings_filament_mapping_save_label": "Enregistrer le mappage",
|
||||
"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",
|
||||
"settings_mqtt_port": "Port MQTT",
|
||||
"settings_mqtt_username_placeholder": "userXXXXXXXX",
|
||||
"settings_orca_profiles_import": "Importer des profils",
|
||||
"settings_orca_profiles_label": "Profils OrcaSlicer",
|
||||
"settings_password": "Mot de passe MQTT",
|
||||
"settings_poll": "Intervalle de sondage (secondes)",
|
||||
"settings_poll_interval_hint": "Fréquence à laquelle le bridge interroge l'état de l'imprimante",
|
||||
"settings_poll_interval_label": "Intervalle de sondage (secondes)",
|
||||
"settings_print": "Paramètres d'impression",
|
||||
"settings_printer_ip": "IP de l'imprimante",
|
||||
"settings_printer_name": "Nom de l'imprimante",
|
||||
"settings_printer_name_placeholder": "p. ex. Kobra X Salon",
|
||||
"settings_save": "Enregistrer et redémarrer",
|
||||
"settings_slot_auto": "Auto (tous les slots chargés)",
|
||||
"settings_theme_toggle": "Basculer clair / sombre",
|
||||
"settings_title": "Paramètres",
|
||||
"settings_username": "Nom d'utilisateur MQTT",
|
||||
"settings_vendor_filter_placeholder": "Rechercher des fabricants…",
|
||||
"settings_version": "Version",
|
||||
"settings_visible_vendors": "Fabricants visibles (liste des profils)",
|
||||
"settings_visible_vendors_hint": "Seuls ces fabricants apparaissent dans la liste des profils d'emplacement. Rien de sélectionné = tout afficher. « Generic » et vos propres profils sont toujours visibles.",
|
||||
"settings_visible_vendors_label": "Fabricants visibles (liste des profils)",
|
||||
"settings_visible_vendors_save": "Enregistrer la sélection",
|
||||
"settings_visible_vendors_save_label": "Enregistrer la sélection",
|
||||
"settings_web_upload_warning": "Afficher un avertissement lors de l'impression de fichiers web",
|
||||
"sf_all": "Tout",
|
||||
"sf_err": "✗ Échoués",
|
||||
"sf_new": "Nouveau",
|
||||
"sf_ok": "✓ Terminés",
|
||||
"skip_already": "ignoré",
|
||||
"skip_btn_label": "Objets",
|
||||
"skip_cancel": "Annuler",
|
||||
"skip_confirm": "Ignorer",
|
||||
"skip_confirm_btn": "Ignorer",
|
||||
"skip_hint": "Décochez les objets que vous ne souhaitez plus imprimer :",
|
||||
"skip_no_objects": "Aucun objet dans cette impression.",
|
||||
"skip_select_at_least_one": "Veuillez sélectionner au moins un objet.",
|
||||
"skip_sending": "Envoi …",
|
||||
"skip_success": "Les objets seront ignorés.",
|
||||
"skip_title": "✂ Ignorer des objets",
|
||||
"slot_edit_color": "Couleur",
|
||||
"slot_edit_custom": "ex. PLA, PETG, ABS…",
|
||||
"slot_edit_load": "⬇ Charger",
|
||||
"slot_edit_material": "Matériau",
|
||||
"slot_edit_ok": "Slot AMS",
|
||||
"slot_edit_profile": "Profil OrcaSlicer",
|
||||
"slot_edit_profile_default": "— Générique (défaut) —",
|
||||
"slot_edit_profile_hint": "Envoyé lors de la synchronisation OrcaSlicer comme marque spécifique au lieu de \"Générique\"",
|
||||
"slot_edit_save": "💾 Enregistrer",
|
||||
"slot_edit_title": "Modifier le slot",
|
||||
"slot_edit_unload": "⬆ Décharger",
|
||||
"speed_normal": "⚡ Normal",
|
||||
"speed_silent": "🐢 Silencieux",
|
||||
"speed_sport": "🚀 Sport",
|
||||
"ss_date": "↓ Date",
|
||||
"ss_dur": "⏱ Durée d'impression",
|
||||
"ss_name": "A–Z Nom",
|
||||
"store_delete_confirm": "Supprimer le fichier ?",
|
||||
"store_download": "⬇ Télécharger",
|
||||
"store_empty": "Aucun fichier uploadé.",
|
||||
"store_estimate": "Estimation",
|
||||
"store_never": "jamais imprimé",
|
||||
"store_no_results": "Aucun fichier trouvé.",
|
||||
"store_print": "▶ Imprimer",
|
||||
"store_print_confirm": "Imprimer le fichier ?",
|
||||
"store_refresh": "↻ Actualiser",
|
||||
"store_search_placeholder": "🔍 Rechercher…",
|
||||
"store_upload_busy": "⏳ Envoi en cours…",
|
||||
"store_upload_error": "✗ {error}",
|
||||
"store_upload_label_browse": "parcourir",
|
||||
"store_upload_label_prefix": "Déposez un GCode ici ou ",
|
||||
"store_upload_only_gcode": "✗ Seuls les fichiers GCode sont autorisés (.gcode, .3mf, .bgcode)",
|
||||
"store_upload_success": "✓ {file}",
|
||||
"store_web_verify_abort": "Annuler",
|
||||
"store_web_verify_confirm": "Confirmer",
|
||||
"store_web_verify_msg": "Veuillez vérifier que ce fichier a été créé pour Anycubic Kobra X.",
|
||||
"store_web_verify_title": "Vérifier le fichier",
|
||||
"update_apply": "Installer maintenant",
|
||||
"update_applying": "Téléchargement…",
|
||||
"update_available": "disponible",
|
||||
"update_check": "Vérifier les mises à jour",
|
||||
"update_checking": "Vérification…",
|
||||
"update_docker": "Copier la commande",
|
||||
"update_docker_copied": "Copié ! Exécuter : docker compose pull && docker compose up -d",
|
||||
"update_error": "Erreur",
|
||||
"update_none": "Déjà à jour",
|
||||
"update_restarting": "Redémarrage…"
|
||||
}
|
||||
329
web/translations/it.json
Normal file
329
web/translations/it.json
Normal file
@@ -0,0 +1,329 @@
|
||||
{
|
||||
"ace_dry_auto_refill": "Ricarica automatica",
|
||||
"ace_dry_chart": "Cronologia (Temp/Umidità)",
|
||||
"ace_dry_current_temp": "Temperatura",
|
||||
"ace_dry_dialog_cancel": "Annulla",
|
||||
"ace_dry_dialog_confirm": "Conferma",
|
||||
"ace_dry_dialog_custom_name": "Nome personalizzato",
|
||||
"ace_dry_dialog_reset_default": "Ripristina predefiniti",
|
||||
"ace_dry_dialog_save_restart": "Salva e riavvia",
|
||||
"ace_dry_dialog_temp": "Temperatura (30-80°C)",
|
||||
"ace_dry_dialog_time": "Tempo rim. (h:m:s)",
|
||||
"ace_dry_dialog_title": "Impostazioni Temp/Tempo essiccatore",
|
||||
"ace_dry_dryer": "Essiccatore",
|
||||
"ace_dry_duration": "Durata (min)",
|
||||
"ace_dry_enable": "Abilita essiccazione",
|
||||
"ace_dry_humidity": "Umidità",
|
||||
"ace_dry_preset_abs_asa": "ABS / ASA",
|
||||
"ace_dry_preset_custom": "Personalizzato",
|
||||
"ace_dry_preset_pa_pc": "PA / PC",
|
||||
"ace_dry_preset_petg": "PETG",
|
||||
"ace_dry_preset_pla": "PLA",
|
||||
"ace_dry_preset_pla_plus": "PLA+",
|
||||
"ace_dry_preset_tpu": "TPU",
|
||||
"ace_dry_start": "▶ Avvia",
|
||||
"ace_dry_status_off": "Stato: Spento",
|
||||
"ace_dry_status_on": "Stato: Attivo",
|
||||
"ace_dry_status_remaining": "Rimanente",
|
||||
"ace_dry_stop": "■ Ferma",
|
||||
"ace_dry_temp": "Temperatura (°C)",
|
||||
"ace_dry_temp_line": "Temperatura di essiccazione",
|
||||
"ace_dry_time_line": "Tempo di essiccazione",
|
||||
"ace_dry_ui_pending": "(Solo interfaccia, backend a seguire)",
|
||||
"add_printer": "Aggiungi stampante",
|
||||
"ams_empty": "Vuoto",
|
||||
"ams_no_data": "Nessun dato ricevuto dall' AMS",
|
||||
"apd_cancel": "Annulla",
|
||||
"apd_confirm": "Aggiungi",
|
||||
"apd_err_ip": "Inserisci un indirizzo IP",
|
||||
"apd_fetching": "Recupero dati dalla stampante…",
|
||||
"apd_lbl_ip": "IP stampante",
|
||||
"apd_lbl_name": "Nome (opzionale)",
|
||||
"apd_placeholder_name": "es. Kobra X Soggiorno",
|
||||
"apd_success": "Stampante aggiunta, riavvio del bridge in corso…",
|
||||
"apd_title": "Aggiungi stampante",
|
||||
"btn_cam_start": "▶ Camera",
|
||||
"btn_cam_start2": "▶ Avvia",
|
||||
"btn_cam_stop": "◼ Camera",
|
||||
"btn_cam_stop2": "◼ Ferma",
|
||||
"btn_cancel": "✕ Stop",
|
||||
"btn_cancel_generic": "Annulla",
|
||||
"btn_confirm_generic": "Conferma",
|
||||
"btn_connect": "⚡ Connetti",
|
||||
"btn_delete": "Elimina",
|
||||
"btn_disable_motors": "Spegni motori",
|
||||
"btn_disconnect": "✕ Disconnetti",
|
||||
"btn_home_all": "Home generale",
|
||||
"btn_home_xy": "Home XY",
|
||||
"btn_home_z": "Home Z",
|
||||
"btn_pause": "⏸ Pausa",
|
||||
"btn_resume": "▶ Riprendi",
|
||||
"cam_placeholder": "📷 Camera non avviata",
|
||||
"cam_stream_unavailable": "Flusso video non disponibile",
|
||||
"card_ace_dry": "Essiccazione ACE",
|
||||
"card_ams": "Filamento",
|
||||
"card_cam": "Camera",
|
||||
"card_light_fan": "Ventola",
|
||||
"card_progress": "Avanzamento",
|
||||
"card_speed": "Velocità di stampa",
|
||||
"card_temps": "Temperature",
|
||||
"confirm_cancel": "Annullare davvero la stampa?",
|
||||
"fd_cancel": "Annulla",
|
||||
"fd_no_matching_material": "Nessun materiale corrispondente",
|
||||
"fd_no_slots_msg": "Nessuno slot AMS caricato.{br}Avviare comunque la stampa?",
|
||||
"fd_objects_hint": "Salta oggetti (opzionale):",
|
||||
"fd_objects_toggle": "Salta oggetti",
|
||||
"fd_options_title": "Opzioni di stampa",
|
||||
"fd_print": "▶ Stampa",
|
||||
"fd_slot": "Slot",
|
||||
"fd_slots_hint": "Assegna il canale GCode allo slot AMS:",
|
||||
"fd_title": "Assegnazione slot",
|
||||
"fd_used": "USATO",
|
||||
"file_cancel_btn": "✕ Annulla",
|
||||
"file_ready_btn": "▶ Avvia stampa",
|
||||
"file_slots_btn": "🎨 Seleziona slot",
|
||||
"header_status_complete": "Completato",
|
||||
"header_status_error": "Errore",
|
||||
"header_status_printing": "In stampa",
|
||||
"header_status_standby": "Pronto",
|
||||
"hint_ip_no_port": "Solo indirizzo IP, senza porta (es. 192.168.1.102)",
|
||||
"kobra_auto_leveling": "Livellamento automatico",
|
||||
"kobra_busy": "Occupato",
|
||||
"kobra_canceled": "Annullato",
|
||||
"kobra_checking": "Verifica",
|
||||
"kobra_failed": "Errore",
|
||||
"kobra_finished": "Finito",
|
||||
"kobra_free": "Pronto",
|
||||
"kobra_init": "Inizializzazione",
|
||||
"kobra_offline": "Offline",
|
||||
"kobra_paused": "In pausa",
|
||||
"kobra_pausing": "Pausa in corso...",
|
||||
"kobra_preheating": "Preriscaldamento",
|
||||
"kobra_printing": "In stampa",
|
||||
"kobra_resumed": "Ripreso",
|
||||
"kobra_resuming": "Ripresa...",
|
||||
"kobra_stoped": "Arrestato",
|
||||
"kobra_stopping": "Arresto...",
|
||||
"kobra_updated": "Aggiornamento",
|
||||
"label_bed": "Piatto",
|
||||
"label_fan": "🌀 Ventola",
|
||||
"label_light": "💡 Luce",
|
||||
"label_nozzle": "Ugello",
|
||||
"label_off": "Off",
|
||||
"label_on_off": "On / Off",
|
||||
"label_set": "Imposta",
|
||||
"label_slot": "Slot",
|
||||
"label_speed": "Velocità",
|
||||
"label_step": "Ampiezza passo:",
|
||||
"label_target_c": "Target:",
|
||||
"lbl_conn_error": "Errore di connessione:",
|
||||
"lbl_elapsed": "Trascorso:",
|
||||
"lbl_feed": "Carica",
|
||||
"lbl_layers": "Layer",
|
||||
"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",
|
||||
"log_axis": "Asse",
|
||||
"log_bed": "Piatto →",
|
||||
"log_cam_start": "Camera avviata:",
|
||||
"log_cam_stop": "Camera arrestata",
|
||||
"log_clear": "✕ Cancella",
|
||||
"log_delete_failed": "Eliminazione non riuscita",
|
||||
"log_dir_all": "Tutti",
|
||||
"log_dir_label": "Dir:",
|
||||
"log_dir_rx": "RX",
|
||||
"log_dir_tx": "TX",
|
||||
"log_download": "⬇ Scarica",
|
||||
"log_error": "Errore:",
|
||||
"log_fan": "Ventola →",
|
||||
"log_filter_placeholder": "Filtra…",
|
||||
"log_home": "Home",
|
||||
"log_home_all": "Home generale",
|
||||
"log_light_off": "Luce spenta",
|
||||
"log_light_on": "Luce accesa",
|
||||
"log_lvl_err": "⛔ Errori",
|
||||
"log_lvl_label": "Livello:",
|
||||
"log_lvl_warn": "⚠ Avvisi",
|
||||
"log_nozzle": "Ugello →",
|
||||
"log_poll_error": "Errore di sincronizzazione:",
|
||||
"log_print_action": "Stampa:",
|
||||
"log_print_start": "Inizio stampa:",
|
||||
"log_topic_ams": "AMS",
|
||||
"log_topic_info": "Info",
|
||||
"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",
|
||||
"nav_dashboard": "Dashboard",
|
||||
"nav_extras": "Luce / Ventola",
|
||||
"nav_motion": "Movimento",
|
||||
"nav_print": "Stampa",
|
||||
"nav_printers": "Stampanti",
|
||||
"nav_settings": "Impostazioni",
|
||||
"nav_temps": "Temperature",
|
||||
"orca_profile_done": "Importato",
|
||||
"orca_profile_dropmsg": "Trascina qui o fai clic",
|
||||
"orca_profile_help_html": "Carica un file <b>ZIP</b> della tua cartella filamenti di OrcaSlicer o file singoli <b>.json</b>.<br>In OrcaSlicer: <i>Aiuto → Mostra cartella di configurazione → user/<id>/filament/</i>",
|
||||
"orca_profile_hint": "Importa i tuoi profili di filamento OrcaSlicer (apri la cartella utente tramite Aiuto → Mostra cartella di configurazione)",
|
||||
"orca_profile_import_btn": "Importa profili",
|
||||
"orca_profile_import_link": "★ Importa i tuoi profili…",
|
||||
"orca_profile_import_title": "Importa i tuoi profili OrcaSlicer",
|
||||
"orca_profile_list_label": "Attualmente importati",
|
||||
"orca_profile_section": "Profili OrcaSlicer",
|
||||
"orca_profile_skipped": "saltato",
|
||||
"orca_profile_uploading": "Caricamento in corso…",
|
||||
"orca_profile_user_empty": "– nessuno –",
|
||||
"orca_profile_user_label": "Profili personali",
|
||||
"panel_ams_title": "Filamento",
|
||||
"panel_browser_title": "Browser dei file",
|
||||
"panel_console_title": "Registro eventi",
|
||||
"panel_extras_camera": "Camera",
|
||||
"panel_extras_fan": "Ventola",
|
||||
"panel_extras_light": "Luce",
|
||||
"panel_motion_xy": "Assi XY",
|
||||
"panel_motion_z": "Asse Z",
|
||||
"panel_print_btn_cancel": "✕ Annulla",
|
||||
"panel_print_btn_pause": "⏸ Pausa",
|
||||
"panel_print_btn_resume": "▶ Riprendi",
|
||||
"panel_print_temps_live": "Temperature (In tempo reale)",
|
||||
"panel_print_title": "Controllo stampa",
|
||||
"panel_temps_bed": "Piatto riscaldato",
|
||||
"panel_temps_chart": "Cronologia (ultime 60 letture)",
|
||||
"panel_temps_nozzle": "Ugello",
|
||||
"print_auto_leveling": "Livellamento automatico",
|
||||
"printers_active": "● attiva",
|
||||
"printers_current": "Stampante corrente",
|
||||
"printers_empty_hint": "Nessuna stampante ancora configurata.",
|
||||
"printers_loading": "Caricamento in corso…",
|
||||
"printers_none": "Nessuna stampante configurata.",
|
||||
"printers_remove": "Rimuovi stampante",
|
||||
"printers_remove_confirm": "Rimuovere la stampante \"{name}\"? Il bridge si riavvierà.",
|
||||
"printers_switch": "Cambia →",
|
||||
"progress_action_clear": "Cancella",
|
||||
"progress_action_print": "Stampa",
|
||||
"progress_action_slots": "Mappa slot",
|
||||
"settings_auto_leveling": "Livellamento automatico predefinito",
|
||||
"settings_auto_leveling_label": "Livellamento automatico prima della stampa",
|
||||
"settings_btn_tooltip": "Impostazioni",
|
||||
"settings_camera_on_print": "Attiva la camera all'avvio della stampa",
|
||||
"settings_cat_connection": "Connessione",
|
||||
"settings_cat_display": "Aspetto",
|
||||
"settings_cat_filament": "Filamento",
|
||||
"settings_cat_language": "Lingua",
|
||||
"settings_cat_printer": "Stampante",
|
||||
"settings_cat_system": "Sistema",
|
||||
"settings_cat_theme": "Alterna chiaro / scuro",
|
||||
"settings_connection": "Connessione",
|
||||
"settings_default_slot": "Slot predefinito (colore singolo)",
|
||||
"settings_device_id": "ID dispositivo",
|
||||
"settings_device_id_hint": "32 caratteri esadecimali",
|
||||
"settings_device_id_placeholder": "32 caratteri esadecimali",
|
||||
"settings_filament_mapping": "Mappatura profilo filamento (per slot)",
|
||||
"settings_filament_mapping_hint": "Profilo Orca fisso per slot AMS. Durante la sincronizzazione dello slicer, il bridge invia questo profilo al posto di \"Generic\".",
|
||||
"settings_filament_mapping_label": "Mappatura profilo filamento (per slot)",
|
||||
"settings_filament_mapping_save": "Salva mappatura",
|
||||
"settings_filament_mapping_save_label": "Salva mappatura",
|
||||
"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",
|
||||
"settings_mqtt_port": "Porta MQTT",
|
||||
"settings_mqtt_username_placeholder": "userXXXXXXXX",
|
||||
"settings_orca_profiles_import": "Importa profili",
|
||||
"settings_orca_profiles_label": "Profili OrcaSlicer",
|
||||
"settings_password": "Password MQTT",
|
||||
"settings_poll": "Intervallo di sincronizzazione (secondi)",
|
||||
"settings_poll_interval_hint": "Con che frequenza il bridge interroga lo stato della stampante",
|
||||
"settings_poll_interval_label": "Intervallo di sincronizzazione (secondi)",
|
||||
"settings_print": "Impostazioni di stampa",
|
||||
"settings_printer_ip": "IP stampante",
|
||||
"settings_printer_name": "Nome stampante",
|
||||
"settings_printer_name_placeholder": "p. es. Kobra X Sala",
|
||||
"settings_save": "Salva e riavvia",
|
||||
"settings_slot_auto": "Auto (tutti gli slot caricati)",
|
||||
"settings_theme_toggle": "Attiva/disattiva chiaro / scuro",
|
||||
"settings_title": "Impostazioni",
|
||||
"settings_username": "Nome utente MQTT",
|
||||
"settings_vendor_filter_placeholder": "Cerca produttori…",
|
||||
"settings_version": "Versione",
|
||||
"settings_visible_vendors": "Produttori visibili (menu del profilo)",
|
||||
"settings_visible_vendors_hint": "Solo questi produttori appariranno nel menu del profilo dello slot. Se non selezioni nulla = mostra tutti. I profili \"Generici\" e i tuoi personali sono sempre visibili.",
|
||||
"settings_visible_vendors_label": "Produttori visibili (menu del profilo)",
|
||||
"settings_visible_vendors_save": "Salva selezione",
|
||||
"settings_visible_vendors_save_label": "Salva selezione",
|
||||
"settings_web_upload_warning": "Mostra un avviso quando si stampano caricamenti web",
|
||||
"sf_all": "Tutti",
|
||||
"sf_err": "✗ Fallito",
|
||||
"sf_new": "Nuovo",
|
||||
"sf_ok": "✓ Completato",
|
||||
"skip_already": "saltato",
|
||||
"skip_btn_label": "Oggetti",
|
||||
"skip_cancel": "Annulla",
|
||||
"skip_confirm": "Salta",
|
||||
"skip_confirm_btn": "Salta",
|
||||
"skip_hint": "Deseleziona gli oggetti che non vuoi più stampare:",
|
||||
"skip_no_objects": "Nessun oggetto in questa stampa.",
|
||||
"skip_select_at_least_one": "Seleziona almeno un oggetto.",
|
||||
"skip_sending": "Invio in corso …",
|
||||
"skip_success": "Gli oggetti verranno saltati.",
|
||||
"skip_title": "✂ Salta oggetti",
|
||||
"slot_edit_color": "Colore",
|
||||
"slot_edit_custom": "es. PLA, PETG, ABS…",
|
||||
"slot_edit_load": "⬇ Carica",
|
||||
"slot_edit_material": "Materiale",
|
||||
"slot_edit_ok": "Slot AMS",
|
||||
"slot_edit_profile": "Profilo OrcaSlicer",
|
||||
"slot_edit_profile_default": "— Generico (predefinito) —",
|
||||
"slot_edit_profile_hint": "Inviato durante la sincronizzazione con OrcaSlicer come marchio specifico invece di un semplice \"Generico\"",
|
||||
"slot_edit_save": "💾 Salva",
|
||||
"slot_edit_title": "Modifica slot",
|
||||
"slot_edit_unload": "⬆ Rimuovi",
|
||||
"speed_normal": "⚡ Normale",
|
||||
"speed_silent": "🐢 Silenzioso",
|
||||
"speed_sport": "🚀 Sport",
|
||||
"ss_date": "↓ Data",
|
||||
"ss_dur": "⏱ Tempo di stampa",
|
||||
"ss_name": "Nome A–Z",
|
||||
"store_delete_confirm": "Eliminare il file?",
|
||||
"store_download": "⬇ Scarica",
|
||||
"store_empty": "Nessun file caricato.",
|
||||
"store_estimate": "Stima",
|
||||
"store_never": "mai stampato",
|
||||
"store_no_results": "Nessun file trovato.",
|
||||
"store_print": "▶ Stampa",
|
||||
"store_print_confirm": "Stampare il file?",
|
||||
"store_refresh": "↻ Aggiorna",
|
||||
"store_search_placeholder": "🔍 Cerca…",
|
||||
"store_upload_busy": "⏳ Caricamento in corso…",
|
||||
"store_upload_error": "✗ {error}",
|
||||
"store_upload_label_browse": "sfoglia",
|
||||
"store_upload_label_prefix": "Trascina il GCode qui o ",
|
||||
"store_upload_only_gcode": "✗ Sono consentiti solo file GCode (.gcode, .3mf, .bgcode)",
|
||||
"store_upload_success": "✓ {file}",
|
||||
"store_web_verify_abort": "Annulla",
|
||||
"store_web_verify_confirm": "Conferma",
|
||||
"store_web_verify_msg": "Verifica che questo file sia stato creato per Anycubic Kobra X.",
|
||||
"store_web_verify_title": "Verifica file",
|
||||
"update_apply": "Installa ora",
|
||||
"update_applying": "Download in corso...",
|
||||
"update_available": "disponibile",
|
||||
"update_check": "Controlla aggiornamenti",
|
||||
"update_checking": "Verifica in corso...",
|
||||
"update_docker": "Copia comando",
|
||||
"update_docker_copied": "Copiato! Eseguire: docker compose pull && docker compose up -d",
|
||||
"update_error": "Errore",
|
||||
"update_none": "Già aggiornato",
|
||||
"update_restarting": "Riavvio in corso..."
|
||||
}
|
||||
@@ -1,247 +1,329 @@
|
||||
{
|
||||
"header_status_standby": "就绪",
|
||||
"header_status_printing": "打印中",
|
||||
"header_status_complete": "完成",
|
||||
"header_status_error": "错误",
|
||||
"kobra_free": "就绪",
|
||||
"kobra_busy": "忙碌",
|
||||
"kobra_printing": "打印中",
|
||||
"kobra_preheating": "预热中",
|
||||
"kobra_auto_leveling": "自动调平",
|
||||
"kobra_checking": "检查中",
|
||||
"kobra_updated": "更新中",
|
||||
"kobra_init": "初始化中",
|
||||
"kobra_pausing": "暂停中...",
|
||||
"kobra_paused": "已暂停",
|
||||
"kobra_resuming": "恢复中...",
|
||||
"kobra_resumed": "已恢复",
|
||||
"kobra_stopping": "停止中...",
|
||||
"kobra_stoped": "已停止",
|
||||
"kobra_finished": "已完成",
|
||||
"kobra_failed": "错误",
|
||||
"kobra_canceled": "已取消",
|
||||
"kobra_offline": "离线",
|
||||
"nav_dashboard": "仪表盘",
|
||||
"nav_print": "打印",
|
||||
"nav_temps": "温度",
|
||||
"nav_motion": "运动",
|
||||
"nav_ams": "AMS",
|
||||
"nav_extras": "灯光 / 风扇",
|
||||
"nav_console": "控制台",
|
||||
"card_progress": "进度",
|
||||
"card_temps": "温度",
|
||||
"card_light_fan": "风扇",
|
||||
"card_speed": "打印速度",
|
||||
"card_cam": "相机",
|
||||
"lbl_elapsed": "已用时间:",
|
||||
"lbl_remaining": "剩余时间:",
|
||||
"lbl_slicer_time": "切片预估:",
|
||||
"lbl_layers": "层",
|
||||
"speed_silent": "🐢 静音",
|
||||
"speed_normal": "⚡ 标准",
|
||||
"speed_sport": "🚀 运动",
|
||||
"lbl_light": "💡 灯光",
|
||||
"lbl_feed": "进料",
|
||||
"lbl_unload": "退料",
|
||||
"card_ace_dry": "ACE 烘干",
|
||||
"ace_dry_auto_refill": "自动补料",
|
||||
"ace_dry_chart": "历史 (温度/湿度)",
|
||||
"ace_dry_current_temp": "温度",
|
||||
"ace_dry_dialog_cancel": "取消",
|
||||
"ace_dry_dialog_confirm": "确认",
|
||||
"ace_dry_dialog_custom_name": "自定义名称",
|
||||
"ace_dry_dialog_reset_default": "恢复默认",
|
||||
"ace_dry_dialog_save_restart": "保存并重启",
|
||||
"ace_dry_dialog_temp": "温度 (30-80°C)",
|
||||
"ace_dry_dialog_time": "剩余时间 (h:m:s)",
|
||||
"ace_dry_dialog_title": "烘干温度/时间设置",
|
||||
"ace_dry_dryer": "烘干机",
|
||||
"ace_dry_duration": "时长 (分钟)",
|
||||
"ace_dry_enable": "启用烘干",
|
||||
"ace_dry_humidity": "湿度",
|
||||
"ace_dry_preset_abs_asa": "ABS / ASA",
|
||||
"ace_dry_preset_custom": "自定义",
|
||||
"ace_dry_preset_pa_pc": "PA / PC",
|
||||
"ace_dry_preset_petg": "PETG",
|
||||
"ace_dry_preset_pla": "PLA",
|
||||
"ace_dry_preset_pla_plus": "PLA+",
|
||||
"ace_dry_preset_tpu": "TPU",
|
||||
"ace_dry_start": "▶ 启动",
|
||||
"ace_dry_status_off": "状态: 关闭",
|
||||
"ace_dry_status_on": "状态: 运行中",
|
||||
"ace_dry_status_remaining": "剩余",
|
||||
"ace_dry_humidity": "湿度",
|
||||
"ace_dry_current_temp": "温度",
|
||||
"ace_dry_chart": "历史 (温度/湿度)",
|
||||
"ace_dry_temp": "温度 (°C)",
|
||||
"ace_dry_duration": "时长 (分钟)",
|
||||
"ace_dry_start": "▶ 启动",
|
||||
"ace_dry_stop": "■ 停止",
|
||||
"ace_dry_auto_refill": "自动补料",
|
||||
"ace_dry_enable": "启用烘干",
|
||||
"ace_dry_temp": "温度 (°C)",
|
||||
"ace_dry_temp_line": "烘干温度",
|
||||
"ace_dry_time_line": "烘干时间",
|
||||
"ace_dry_ui_pending": "(仅 UI,后端稍后支持)",
|
||||
"ace_dry_dialog_title": "烘干温度/时间设置",
|
||||
"ace_dry_dialog_temp": "温度 (30-80°C)",
|
||||
"ace_dry_dialog_time": "剩余时间 (h:m:s)",
|
||||
"ace_dry_dialog_confirm": "确认",
|
||||
"ace_dry_dialog_cancel": "取消",
|
||||
"ace_dry_dialog_save_restart": "保存并重启",
|
||||
"ace_dry_dialog_custom_name": "自定义名称",
|
||||
"ace_dry_dialog_reset_default": "恢复默认",
|
||||
"cam_placeholder": "📷 相机未启动",
|
||||
"cam_stream_unavailable": "视频流不可用",
|
||||
"add_printer": "添加打印机",
|
||||
"ams_empty": "空",
|
||||
"ams_no_data": "未收到 AMS 数据",
|
||||
"apd_cancel": "取消",
|
||||
"apd_confirm": "添加",
|
||||
"apd_err_ip": "请输入 IP 地址",
|
||||
"apd_fetching": "正在从打印机获取数据…",
|
||||
"apd_lbl_ip": "打印机 IP",
|
||||
"apd_lbl_name": "名称 (可选)",
|
||||
"apd_placeholder_name": "例如 Kobra X 客厅",
|
||||
"apd_success": "打印机已添加,Bridge 正在重启…",
|
||||
"apd_title": "添加打印机",
|
||||
"btn_cam_start": "▶ 相机",
|
||||
"btn_cam_start2": "▶ 启动",
|
||||
"btn_cam_stop": "◼ 相机",
|
||||
"btn_cam_stop2": "◼ 停止",
|
||||
"btn_cancel": "✕ 停止",
|
||||
"btn_cancel_generic": "取消",
|
||||
"btn_confirm_generic": "确认",
|
||||
"btn_connect": "⚡ 连接",
|
||||
"btn_delete": "删除",
|
||||
"btn_disable_motors": "关闭电机",
|
||||
"btn_disconnect": "✕ 断开",
|
||||
"btn_home_all": "全部回零",
|
||||
"btn_home_xy": "回零 XY",
|
||||
"btn_home_z": "回零 Z",
|
||||
"btn_pause": "⏸ 暂停",
|
||||
"btn_resume": "▶ 继续",
|
||||
"btn_cancel": "✕ 停止",
|
||||
"label_nozzle": "喷嘴",
|
||||
"cam_placeholder": "📷 相机未启动",
|
||||
"cam_stream_unavailable": "视频流不可用",
|
||||
"card_ace_dry": "ACE 烘干",
|
||||
"card_ams": "耗材",
|
||||
"card_cam": "相机",
|
||||
"card_light_fan": "风扇",
|
||||
"card_progress": "进度",
|
||||
"card_speed": "打印速度",
|
||||
"card_temps": "温度",
|
||||
"confirm_cancel": "确定要取消打印吗?",
|
||||
"fd_cancel": "取消",
|
||||
"fd_no_matching_material": "无匹配材料",
|
||||
"fd_no_slots_msg": "没有已装载的 AMS 槽位。{br}仍要开始打印吗?",
|
||||
"fd_objects_hint": "跳过对象 (可选):",
|
||||
"fd_objects_toggle": "跳过对象",
|
||||
"fd_options_title": "选项",
|
||||
"fd_print": "▶ 打印",
|
||||
"fd_slot": "槽位",
|
||||
"fd_slots_hint": "将 GCode 通道分配到 AMS 槽位:",
|
||||
"fd_title": "槽位分配",
|
||||
"fd_used": "已用",
|
||||
"file_cancel_btn": "✕ 取消",
|
||||
"file_ready_btn": "▶ 开始打印",
|
||||
"file_slots_btn": "🎨 选择槽位",
|
||||
"header_status_complete": "完成",
|
||||
"header_status_error": "错误",
|
||||
"header_status_printing": "打印中",
|
||||
"header_status_standby": "就绪",
|
||||
"hint_ip_no_port": "仅填写 IP,不要端口 (例如 192.168.1.102)",
|
||||
"kobra_auto_leveling": "自动调平",
|
||||
"kobra_busy": "忙碌",
|
||||
"kobra_canceled": "已取消",
|
||||
"kobra_checking": "检查中",
|
||||
"kobra_failed": "错误",
|
||||
"kobra_finished": "已完成",
|
||||
"kobra_free": "就绪",
|
||||
"kobra_init": "初始化中",
|
||||
"kobra_offline": "离线",
|
||||
"kobra_paused": "已暂停",
|
||||
"kobra_pausing": "暂停中...",
|
||||
"kobra_preheating": "预热中",
|
||||
"kobra_printing": "打印中",
|
||||
"kobra_resumed": "已恢复",
|
||||
"kobra_resuming": "恢复中...",
|
||||
"kobra_stoped": "已停止",
|
||||
"kobra_stopping": "停止中...",
|
||||
"kobra_updated": "更新中",
|
||||
"label_bed": "热床",
|
||||
"label_fan": "🌀 风扇",
|
||||
"label_light": "💡 灯光",
|
||||
"label_on_off": "开 / 关",
|
||||
"label_speed": "速度",
|
||||
"panel_print_title": "打印控制",
|
||||
"panel_print_btn_pause": "⏸ 暂停",
|
||||
"panel_print_btn_resume": "▶ 继续",
|
||||
"panel_print_btn_cancel": "✕ 取消",
|
||||
"panel_print_temps_live": "温度 (实时)",
|
||||
"label_set": "设置",
|
||||
"label_nozzle": "喷嘴",
|
||||
"label_off": "关闭",
|
||||
"panel_temps_nozzle": "喷嘴",
|
||||
"panel_temps_bed": "热床",
|
||||
"panel_temps_chart": "历史 (最近 60 次读数)",
|
||||
"label_target_c": "目标:",
|
||||
"panel_motion_xy": "XY 轴",
|
||||
"panel_motion_z": "Z 轴",
|
||||
"label_step": "步进:",
|
||||
"btn_home_z": "回零 Z",
|
||||
"btn_home_xy": "回零 XY",
|
||||
"btn_home_all": "全部回零",
|
||||
"btn_disable_motors": "关闭电机",
|
||||
"panel_ams_title": "耗材",
|
||||
"card_ams": "耗材",
|
||||
"ams_no_data": "未收到 AMS 数据",
|
||||
"label_on_off": "开 / 关",
|
||||
"label_set": "设置",
|
||||
"label_slot": "槽位",
|
||||
"ams_empty": "空",
|
||||
"panel_extras_light": "灯光",
|
||||
"panel_extras_fan": "风扇",
|
||||
"panel_extras_camera": "相机",
|
||||
"btn_cam_start2": "▶ 启动",
|
||||
"btn_cam_stop2": "◼ 停止",
|
||||
"panel_console_title": "事件日志",
|
||||
"log_light_on": "灯光已开",
|
||||
"log_light_off": "灯光已关",
|
||||
"log_fan": "风扇 →",
|
||||
"log_nozzle": "喷嘴 →",
|
||||
"log_bed": "热床 →",
|
||||
"label_speed": "速度",
|
||||
"label_step": "步进:",
|
||||
"label_target_c": "目标:",
|
||||
"lbl_conn_error": "连接错误:",
|
||||
"lbl_elapsed": "已用时间:",
|
||||
"lbl_feed": "进料",
|
||||
"lbl_layers": "层",
|
||||
"lbl_light": "💡 灯光",
|
||||
"lbl_remaining": "剩余时间:",
|
||||
"lbl_slicer_time": "切片预估:",
|
||||
"lbl_spoolman_sync_rate": "同步频率(秒,0=关闭)",
|
||||
"lbl_spoolman_url": "服务器地址",
|
||||
"lbl_unload": "退料",
|
||||
"lbl_zpos": "Z (mm)",
|
||||
"log_auto": "⬇ 自动",
|
||||
"log_axis": "轴",
|
||||
"log_home": "回零",
|
||||
"log_home_all": "全部回零",
|
||||
"log_bed": "热床 →",
|
||||
"log_cam_start": "相机已启动:",
|
||||
"log_cam_stop": "相机已停止",
|
||||
"log_poll_error": "轮询错误:",
|
||||
"log_clear": "✕ 清空",
|
||||
"log_delete_failed": "删除失败",
|
||||
"log_dir_all": "全部",
|
||||
"log_dir_label": "方向:",
|
||||
"log_dir_rx": "RX",
|
||||
"log_dir_tx": "TX",
|
||||
"log_download": "⬇ 下载",
|
||||
"log_error": "错误:",
|
||||
"confirm_cancel": "确定要取消打印吗?",
|
||||
"settings_title": "设置",
|
||||
"settings_connection": "连接",
|
||||
"settings_print": "打印设置",
|
||||
"settings_poll": "轮询间隔",
|
||||
"settings_version": "版本",
|
||||
"settings_save": "保存并重启",
|
||||
"settings_printer_name": "打印机名称",
|
||||
"settings_printer_ip": "打印机 IP",
|
||||
"settings_mqtt_port": "MQTT 端口",
|
||||
"settings_username": "MQTT 用户名",
|
||||
"settings_password": "MQTT 密码",
|
||||
"settings_device_id": "设备 ID",
|
||||
"settings_mode_id": "模式 ID",
|
||||
"hint_ip_no_port": "仅填写 IP,不要端口 (例如 192.168.1.102)",
|
||||
"settings_default_slot": "默认槽位 (单色)",
|
||||
"settings_slot_auto": "自动 (所有已装载槽位)",
|
||||
"settings_auto_leveling": "打印前自动调平",
|
||||
"settings_camera_on_print": "打印开始时开启相机",
|
||||
"settings_web_upload_warning": "打印网页上传文件时显示警告",
|
||||
"update_check": "检查更新",
|
||||
"update_checking": "检查中...",
|
||||
"update_available": "可用",
|
||||
"update_none": "已是最新版本",
|
||||
"update_apply": "立即安装",
|
||||
"update_applying": "下载中...",
|
||||
"update_restarting": "重启中...",
|
||||
"update_error": "错误",
|
||||
"btn_connect": "⚡ 连接",
|
||||
"btn_disconnect": "✕ 断开",
|
||||
"lbl_conn_error": "连接错误:",
|
||||
"slot_edit_title": "编辑槽位",
|
||||
"slot_edit_color": "颜色",
|
||||
"slot_edit_material": "材料",
|
||||
"slot_edit_load": "⬇ 进料",
|
||||
"slot_edit_unload": "⬆ 退料",
|
||||
"slot_edit_save": "💾 保存",
|
||||
"slot_edit_custom": "例如 PLA, PETG, ABS…",
|
||||
"slot_edit_ok": "AMS 槽位",
|
||||
"slot_edit_profile": "OrcaSlicer 配置",
|
||||
"slot_edit_profile_hint": "在 OrcaSlicer 同步时发送具体品牌,而不仅仅是“Generic”",
|
||||
"slot_edit_profile_default": "— 通用 (默认) —",
|
||||
"orca_profile_section": "OrcaSlicer 配置",
|
||||
"log_fan": "风扇 →",
|
||||
"log_filter_placeholder": "筛选…",
|
||||
"log_home": "回零",
|
||||
"log_home_all": "全部回零",
|
||||
"log_light_off": "灯光已关",
|
||||
"log_light_on": "灯光已开",
|
||||
"log_lvl_err": "⛔ 错误",
|
||||
"log_lvl_label": "级别:",
|
||||
"log_lvl_warn": "⚠ 警告",
|
||||
"log_nozzle": "喷嘴 →",
|
||||
"log_poll_error": "轮询错误:",
|
||||
"log_print_action": "打印:",
|
||||
"log_print_start": "打印开始:",
|
||||
"log_topic_ams": "AMS",
|
||||
"log_topic_info": "信息",
|
||||
"log_topic_label": "主题:",
|
||||
"log_topic_print": "打印",
|
||||
"log_topic_status": "状态",
|
||||
"modal_sec_obico": "Obico",
|
||||
"modal_sec_spoolman": "Spoolman",
|
||||
"nav_ams": "AMS",
|
||||
"nav_browser": "浏览器",
|
||||
"nav_console": "控制台",
|
||||
"nav_dashboard": "仪表盘",
|
||||
"nav_extras": "灯光 / 风扇",
|
||||
"nav_motion": "运动",
|
||||
"nav_print": "打印",
|
||||
"nav_printers": "打印机",
|
||||
"nav_settings": "设置",
|
||||
"nav_temps": "温度",
|
||||
"orca_profile_done": "已导入",
|
||||
"orca_profile_dropmsg": "拖到此处或点击",
|
||||
"orca_profile_help_html": "上传 OrcaSlicer 耗材文件夹的 <b>ZIP</b> 或单个 <b>.json</b> 文件。<br>在 OrcaSlicer 中: <i>Help → Show Configuration Folder → user/<id>/filament/</i>",
|
||||
"orca_profile_hint": "导入你自己的 OrcaSlicer 耗材配置(在 Help → Show Configuration Folder 打开用户目录)",
|
||||
"orca_profile_import_btn": "导入配置",
|
||||
"orca_profile_import_link": "★ 导入自己的配置…",
|
||||
"orca_profile_import_title": "导入你的 OrcaSlicer 配置",
|
||||
"orca_profile_help_html": "上传 OrcaSlicer 耗材文件夹的 <b>ZIP</b> 或单个 <b>.json</b> 文件。<br>在 OrcaSlicer 中: <i>Help → Show Configuration Folder → user/<id>/filament/</i>",
|
||||
"orca_profile_dropmsg": "拖到此处或点击",
|
||||
"orca_profile_list_label": "已导入",
|
||||
"orca_profile_user_label": "自己的配置",
|
||||
"orca_profile_user_empty": "– 无 –",
|
||||
"orca_profile_uploading": "上传中…",
|
||||
"orca_profile_done": "已导入",
|
||||
"orca_profile_section": "OrcaSlicer 配置",
|
||||
"orca_profile_skipped": "跳过",
|
||||
"log_dir_all": "全部",
|
||||
"log_lvl_label": "级别:",
|
||||
"file_ready_btn": "▶ 开始打印",
|
||||
"file_slots_btn": "🎨 选择槽位",
|
||||
"file_cancel_btn": "✕ 取消",
|
||||
"nav_printers": "打印机",
|
||||
"skip_title": "✂ 跳过对象",
|
||||
"skip_hint": "取消勾选不想继续打印的对象:",
|
||||
"skip_btn_label": "对象",
|
||||
"skip_no_objects": "此打印任务没有对象。",
|
||||
"orca_profile_uploading": "上传中…",
|
||||
"orca_profile_user_empty": "– 无 –",
|
||||
"orca_profile_user_label": "自己的配置",
|
||||
"panel_ams_title": "耗材",
|
||||
"panel_browser_title": "文件浏览器",
|
||||
"panel_console_title": "事件日志",
|
||||
"panel_extras_camera": "相机",
|
||||
"panel_extras_fan": "风扇",
|
||||
"panel_extras_light": "灯光",
|
||||
"panel_motion_xy": "XY 轴",
|
||||
"panel_motion_z": "Z 轴",
|
||||
"panel_print_btn_cancel": "✕ 取消",
|
||||
"panel_print_btn_pause": "⏸ 暂停",
|
||||
"panel_print_btn_resume": "▶ 继续",
|
||||
"panel_print_temps_live": "温度 (实时)",
|
||||
"panel_print_title": "打印控制",
|
||||
"panel_temps_bed": "热床",
|
||||
"panel_temps_chart": "历史 (最近 60 次读数)",
|
||||
"panel_temps_nozzle": "喷嘴",
|
||||
"print_auto_leveling": "本次打印自动调平",
|
||||
"printers_active": "● 活动",
|
||||
"printers_current": "当前打印机",
|
||||
"printers_empty_hint": "尚未设置打印机。",
|
||||
"printers_loading": "加载中…",
|
||||
"printers_none": "未配置打印机。",
|
||||
"printers_remove": "移除打印机",
|
||||
"printers_remove_confirm": "移除打印机 \"{name}\"? Bridge 将重启。",
|
||||
"printers_switch": "切换 →",
|
||||
"progress_action_clear": "清除",
|
||||
"progress_action_print": "打印",
|
||||
"progress_action_slots": "分配槽位",
|
||||
"settings_auto_leveling": "打印前自动调平",
|
||||
"settings_auto_leveling_label": "打印前自动调平",
|
||||
"settings_btn_tooltip": "设置",
|
||||
"settings_camera_on_print": "打印开始时开启相机",
|
||||
"settings_cat_connection": "连接",
|
||||
"settings_cat_display": "外观",
|
||||
"settings_cat_filament": "耗材",
|
||||
"settings_cat_language": "语言",
|
||||
"settings_cat_printer": "打印机",
|
||||
"settings_cat_system": "系统",
|
||||
"settings_cat_theme": "切换浅色 / 深色",
|
||||
"settings_connection": "连接",
|
||||
"settings_default_slot": "默认槽位 (单色)",
|
||||
"settings_device_id": "设备 ID",
|
||||
"settings_device_id_hint": "32 个十六进制字符",
|
||||
"settings_device_id_placeholder": "32 个十六进制字符",
|
||||
"settings_filament_mapping": "耗材配置映射(每槽位)",
|
||||
"settings_filament_mapping_hint": "每个 AMS 槽位的固定 Orca 配置。在切片器同步时,Bridge 会发送此配置而不是“Generic”。",
|
||||
"settings_filament_mapping_label": "耗材配置映射(每槽位)",
|
||||
"settings_filament_mapping_save": "保存映射",
|
||||
"settings_filament_mapping_save_label": "保存映射",
|
||||
"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",
|
||||
"settings_mqtt_port": "MQTT 端口",
|
||||
"settings_mqtt_username_placeholder": "userXXXXXXXX",
|
||||
"settings_orca_profiles_import": "导入配置文件",
|
||||
"settings_orca_profiles_label": "OrcaSlicer 配置文件",
|
||||
"settings_password": "MQTT 密码",
|
||||
"settings_poll": "轮询间隔(秒)",
|
||||
"settings_poll_interval_hint": "Bridge 查询打印机状态的频率",
|
||||
"settings_poll_interval_label": "轮询间隔(秒)",
|
||||
"settings_print": "打印设置",
|
||||
"settings_printer_ip": "打印机 IP",
|
||||
"settings_printer_name": "打印机名称",
|
||||
"settings_printer_name_placeholder": "例如 Kobra X 左",
|
||||
"settings_save": "保存并重启",
|
||||
"settings_slot_auto": "自动 (所有已装载槽位)",
|
||||
"settings_theme_toggle": "切换浅色 / 深色",
|
||||
"settings_title": "设置",
|
||||
"settings_username": "MQTT 用户名",
|
||||
"settings_vendor_filter_placeholder": "搜索厂商…",
|
||||
"settings_version": "版本",
|
||||
"settings_visible_vendors": "可见厂商(配置下拉框)",
|
||||
"settings_visible_vendors_hint": "仅这些厂商会出现在槽位配置下拉框中。未选择 = 显示全部。“Generic”和您自己的配置始终可见。",
|
||||
"settings_visible_vendors_label": "可见厂商(配置下拉框)",
|
||||
"settings_visible_vendors_save": "保存选择",
|
||||
"settings_visible_vendors_save_label": "保存选择",
|
||||
"settings_web_upload_warning": "打印网页上传文件时显示警告",
|
||||
"sf_all": "全部",
|
||||
"sf_err": "✗ 失败",
|
||||
"sf_new": "新",
|
||||
"sf_ok": "✓ 已完成",
|
||||
"skip_already": "已跳过",
|
||||
"skip_btn_label": "对象",
|
||||
"skip_cancel": "取消",
|
||||
"skip_confirm": "跳过",
|
||||
"skip_confirm_btn": "跳过",
|
||||
"skip_hint": "取消勾选不想继续打印的对象:",
|
||||
"skip_no_objects": "此打印任务没有对象。",
|
||||
"skip_select_at_least_one": "请至少选择一个对象。",
|
||||
"skip_sending": "发送中 …",
|
||||
"skip_success": "对象将被跳过。",
|
||||
"fd_objects_hint": "跳过对象 (可选):",
|
||||
"fd_slots_hint": "将 GCode 通道分配到 AMS 槽位:",
|
||||
"fd_cancel": "取消",
|
||||
"fd_print": "▶ 打印",
|
||||
"fd_no_slots_msg": "没有已装载的 AMS 槽位。{br}仍要开始打印吗?",
|
||||
"fd_slot": "槽位",
|
||||
"fd_no_matching_material": "无匹配材料",
|
||||
"fd_used": "已用",
|
||||
"add_printer": "添加打印机",
|
||||
"apd_lbl_ip": "打印机 IP",
|
||||
"apd_lbl_name": "名称 (可选)",
|
||||
"apd_placeholder_name": "例如 Kobra X 客厅",
|
||||
"apd_cancel": "取消",
|
||||
"apd_confirm": "添加",
|
||||
"apd_fetching": "正在从打印机获取数据…",
|
||||
"apd_success": "打印机已添加,Bridge 正在重启…",
|
||||
"apd_err_ip": "请输入 IP 地址",
|
||||
"printers_remove": "移除打印机",
|
||||
"printers_remove_confirm": "移除打印机 \"{name}\"? Bridge 将重启。",
|
||||
"printers_active": "● 活动",
|
||||
"printers_switch": "切换 →",
|
||||
"printers_current": "当前打印机",
|
||||
"printers_loading": "加载中…",
|
||||
"printers_none": "未配置打印机。",
|
||||
"printers_empty_hint": "尚未设置打印机。",
|
||||
"nav_browser": "浏览器",
|
||||
"panel_browser_title": "文件浏览器",
|
||||
"store_search_placeholder": "🔍 搜索…",
|
||||
"store_empty": "尚未上传文件。",
|
||||
"store_refresh": "↻ 刷新",
|
||||
"store_print": "▶ 打印",
|
||||
"store_download": "⬇ 下载",
|
||||
"store_delete_confirm": "删除文件?",
|
||||
"store_print_confirm": "打印文件?",
|
||||
"store_web_verify_title": "验证文件",
|
||||
"store_web_verify_msg": "请确认此文件是为 Anycubic Kobra X 创建的。",
|
||||
"store_web_verify_confirm": "确认",
|
||||
"store_web_verify_abort": "取消",
|
||||
"store_no_results": "未找到文件。",
|
||||
"store_never": "从未打印",
|
||||
"store_estimate": "估算",
|
||||
"store_upload_label_prefix": "将 GCode 拖到这里或 ",
|
||||
"store_upload_label_browse": "浏览",
|
||||
"store_upload_busy": "⏳ 上传中…",
|
||||
"store_upload_success": "✓ {file}",
|
||||
"store_upload_error": "✗ {error}",
|
||||
"sf_all": "全部",
|
||||
"sf_ok": "✓ 已完成",
|
||||
"sf_err": "✗ 失败",
|
||||
"sf_new": "新",
|
||||
"skip_title": "✂ 跳过对象",
|
||||
"slot_edit_color": "颜色",
|
||||
"slot_edit_custom": "例如 PLA, PETG, ABS…",
|
||||
"slot_edit_load": "⬇ 进料",
|
||||
"slot_edit_material": "材料",
|
||||
"slot_edit_ok": "AMS 槽位",
|
||||
"slot_edit_profile": "OrcaSlicer 配置",
|
||||
"slot_edit_profile_default": "— 通用 (默认) —",
|
||||
"slot_edit_profile_hint": "在 OrcaSlicer 同步时发送具体品牌,而不仅仅是“Generic”",
|
||||
"slot_edit_save": "💾 保存",
|
||||
"slot_edit_title": "编辑槽位",
|
||||
"slot_edit_unload": "⬆ 退料",
|
||||
"speed_normal": "⚡ 标准",
|
||||
"speed_silent": "🐢 静音",
|
||||
"speed_sport": "🚀 运动",
|
||||
"ss_date": "↓ 日期",
|
||||
"ss_dur": "⏱ 打印时间",
|
||||
"ss_name": "A–Z 名称",
|
||||
"ss_dur": "⏱ 打印时间"
|
||||
"store_delete_confirm": "删除文件?",
|
||||
"store_download": "⬇ 下载",
|
||||
"store_empty": "尚未上传文件。",
|
||||
"store_estimate": "估算",
|
||||
"store_never": "从未打印",
|
||||
"store_no_results": "未找到文件。",
|
||||
"store_print": "▶ 打印",
|
||||
"store_print_confirm": "打印文件?",
|
||||
"store_refresh": "↻ 刷新",
|
||||
"store_search_placeholder": "🔍 搜索…",
|
||||
"store_upload_busy": "⏳ 上传中…",
|
||||
"store_upload_error": "✗ {error}",
|
||||
"store_upload_label_browse": "浏览",
|
||||
"store_upload_label_prefix": "将 GCode 拖到这里或 ",
|
||||
"store_upload_only_gcode": "✗ 仅允许 GCode 文件 (.gcode, .3mf, .bgcode)",
|
||||
"store_upload_success": "✓ {file}",
|
||||
"store_web_verify_abort": "取消",
|
||||
"store_web_verify_confirm": "确认",
|
||||
"store_web_verify_msg": "请验证此文件是为Anycubic Kobra X创建的。",
|
||||
"store_web_verify_title": "验证文件",
|
||||
"update_apply": "立即安装",
|
||||
"update_applying": "下载中...",
|
||||
"update_available": "可用",
|
||||
"update_check": "检查更新",
|
||||
"update_checking": "检查中...",
|
||||
"update_docker": "复制命令",
|
||||
"update_docker_copied": "已复制!执行:docker compose pull && docker compose up -d",
|
||||
"update_error": "错误",
|
||||
"update_none": "已是最新版本",
|
||||
"update_restarting": "重启中..."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user