release: v0.9.8
This commit is contained in:
@@ -1,5 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
## [0.9.8] – 2026-05-12
|
||||
|
||||
### Neu
|
||||
- **Multi-Printer in einer Bridge-Instanz:** Ein Prozess verwaltet jetzt mehrere Drucker gleichzeitig — N MQTT-Verbindungen + N HTTP-Listener (Ports 7125, 7126, …), geteilte SQLite + GCode-Store. Konfiguration über `[printer_1]`-, `[printer_2]`-… Sektionen in `config.ini`. Einzel-Modus (`[connection]`) funktioniert unverändert weiter. `docker-compose.yml` exposed einen Port-Range `7125-7130`.
|
||||
- **Drucker per UI hinzufügen:** „+ Drucker hinzufügen"-Button im Drucker-Tab — nur die IP eingeben, Zugangsdaten (Username, Passwort, Device-ID) werden automatisch vom Drucker geholt und entschlüsselt. Weitere Drucker bekommen den nächsten freien Port (7126, 7127, …).
|
||||
- **Drucker per UI entfernen:** „✕"-Button auf jeder Drucker-Karte mit Bestätigung — entfernt die `[printer_N]`-Sektion und nummeriert die übrigen um. Beim Entfernen des letzten Druckers wird auch `[connection]` geleert (leerer Zustand).
|
||||
- **GCode Store:** Hochgeladene Dateien werden in SQLite gespeichert, inkl. Thumbnail-Extraktion. Neue `/kx/files`-API.
|
||||
- **Browser-Tab:** Grid-Ansicht aller hochgeladenen Dateien — Thumbnail, Status-Badge (✓/✗), letzte Druckdauer, plus Suche, Filter und Sortierung.
|
||||
- **Druckhistorie:** Druckaufträge (Start/Ende/Status) werden in SQLite protokolliert, Status pro Datei im Browser-Tab sichtbar.
|
||||
- **Filament-Dialog:** Per-Kanal-Remapping vor dem Druckstart — jeder GCode-Farbkanal wird einem physischen AMS-Slot zugewiesen (wie im Anycubic Slicer). Verfügbar im Browser-Tab und im Upload-Banner.
|
||||
- **MMU-Emulation:** `GET /printer/objects/query?mmu` liefert eine Happy-Hare-kompatible Struktur, damit OrcaSlicers Filament-Sync die AMS-Slots erkennt.
|
||||
- **Drucker-Tab:** Live-Status aller Drucker-Instanzen, IP auf jeder Karte, „Wechseln →"-Button.
|
||||
- **Editierbarer Drucker-Name:** Eigener Name in den Einstellungen (gespeichert in `[bridge] printer_name`, hat Vorrang vor dem vom Drucker gemeldeten Namen).
|
||||
- **Standalone-tauglich:** Linux-Binary / Windows-EXE laufen ohne Docker — `config/` und `data/` liegen neben dem Programm (portabel). Erststart ohne konfigurierten Drucker zeigt den Drucker-Tab mit „+ Drucker hinzufügen" statt des Einstellungs-Dialogs.
|
||||
- **i18n:** Alle neuen UI-Elemente auf Deutsch und Englisch.
|
||||
|
||||
### Fixes
|
||||
- **CORS:** CORS-Middleware auf allen Endpunkten — Cross-Instance-Fetches in der Multi-Printer-UI funktionieren zuverlässig.
|
||||
- **Einstellungen / Update-Check** zeigen im Multi-Printer-Modus jetzt die aktive Bridge-Instanz (via `_apiUrl`).
|
||||
- **Bridge-Neustart:** Config-abhängige Umgebungsvariablen werden vor einem Neustart gelöscht (der Config-Loader cachte sie, wodurch Config-Änderungen erst nach einem Kaltstart sichtbar wurden). Der Neustart ist jetzt plattformabhängig: Docker/systemd → Prozess-Exit (Supervisor startet neu), Linux standalone → `os.execv`, Windows → detachter Subprozess.
|
||||
- **`--data-dir`-Default** ist jetzt plattformabhängig — der `/app/data`-Default greift nur in Docker (per `ENV` gesetzt), Standalone-Binaries nutzen `<exe-dir>/data`. Behebt einen Startup-Crash beim Ausführen ohne Docker.
|
||||
|
||||
---
|
||||
|
||||
## [0.9.7] – 2026-05-08
|
||||
|
||||
### Neu
|
||||
|
||||
24
CHANGELOG.md
24
CHANGELOG.md
@@ -1,5 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
## [0.9.8] – 2026-05-12
|
||||
|
||||
### New
|
||||
- **Multi-printer in a single bridge instance:** One process now manages multiple printers — N MQTT connections + N HTTP listeners (ports 7125, 7126, …), shared SQLite + GCode store. Configure via `[printer_1]`, `[printer_2]` … sections in `config.ini`. Single-printer mode (`[connection]` only) keeps working unchanged. `docker-compose.yml` exposes a port range `7125-7130`.
|
||||
- **Add printer from the UI:** "+ Add printer" button in the Printers tab — just enter the printer IP, the credentials (username, password, device ID) are fetched and decrypted from the printer automatically. Adding more printers assigns the next free port (7126, 7127, …).
|
||||
- **Remove printer from the UI:** "✕" button on each printer card with a confirmation dialog — removes the `[printer_N]` section and renumbers the rest. Removing the last printer clears `[connection]` too, leaving an empty state.
|
||||
- **GCode Store:** Uploaded files are persisted in SQLite with thumbnail extraction. New `/kx/files` API.
|
||||
- **Browser tab:** Grid view of all uploaded files — thumbnail, status badge (✓/✗), last print duration, plus search, filter and sort.
|
||||
- **Print history:** Print jobs (start/end/status) are recorded in SQLite, status shown per file in the Browser tab.
|
||||
- **Filament dialog:** Per-channel remapping before print start — assign each GCode color channel to a physical AMS slot (like the Anycubic Slicer does). Available in the Browser tab and the upload banner.
|
||||
- **MMU emulation:** `GET /printer/objects/query?mmu` returns a Happy-Hare-compatible structure so OrcaSlicer's filament sync detects the AMS slots.
|
||||
- **Printers tab:** Live status of all printer instances, IP shown on each card, "Switch →" button.
|
||||
- **Editable printer name:** Set a custom name in Settings (stored in `[bridge] printer_name`, takes precedence over the MQTT-reported name).
|
||||
- **Standalone friendly:** Linux binary / Windows EXE run without Docker — `config/` and `data/` are placed next to the executable (portable). First start with no printer configured shows the Printers tab with "+ Add printer" instead of the settings modal.
|
||||
- **i18n:** All new UI elements available in German and English.
|
||||
|
||||
### Fixes
|
||||
- **CORS:** CORS middleware added to all endpoints — cross-instance fetches in the multi-printer UI work reliably.
|
||||
- **Settings / update check** now reflect the active bridge instance in multi-printer mode (via `_apiUrl`).
|
||||
- **Bridge restart:** Config-dependent environment variables are cleared before a restart (the config loader cached them, which made config changes invisible until the next cold start). Restart is now platform-aware: Docker/systemd → process exit (supervisor restarts), Linux standalone → `os.execv`, Windows → detached subprocess.
|
||||
- **`--data-dir` default** is now platform-dependent — the `/app/data` default only applies inside Docker (set via `ENV`), standalone binaries use `<exe-dir>/data`. Fixes a startup crash when running the binary without Docker.
|
||||
|
||||
---
|
||||
|
||||
## [0.9.7] – 2026-05-08
|
||||
|
||||
### New
|
||||
|
||||
@@ -16,7 +16,12 @@ COPY config/config.ini.example /app/config/config.ini.example
|
||||
|
||||
# config/ ist ein Volume-Mountpoint – beim Start wird config.ini aus .env migriert
|
||||
# falls noch keine config.ini vorhanden ist.
|
||||
RUN mkdir -p /app/config
|
||||
RUN mkdir -p /app/config && mkdir -p /app/data
|
||||
|
||||
# Daten-Verzeichnis fest auf /app/data (sonst würde der Binary-Default <exe-dir>/data greifen)
|
||||
# und Container-Erkennung für den Bridge-Restart (Supervisor startet neu statt subprocess).
|
||||
ENV KX_DATA_DIR=/app/data
|
||||
ENV KX_IN_DOCKER=1
|
||||
|
||||
EXPOSE 7125
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# KX-Bridge – Anycubic Kobra X
|
||||
|
||||
**Version:** 0.9.7
|
||||
**Version:** 0.9.8
|
||||
|
||||
Steuere deinen **Anycubic Kobra X** mit OrcaSlicer — ohne Klipper, ohne Raspberry Pi.
|
||||
KX-Bridge ist eine Moonraker-kompatible Bridge die direkt mit dem Drucker kommuniziert.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
# KX-Bridge – Anycubic Kobra X
|
||||
|
||||
**Version:** 0.9.7
|
||||
**Version:** 0.9.8
|
||||
|
||||
Control your **Anycubic Kobra X** with OrcaSlicer — no Klipper, no Raspberry Pi.
|
||||
KX-Bridge is a Moonraker-compatible bridge that communicates directly with the printer.
|
||||
|
||||
36
config/config.ini.example
Normal file
36
config/config.ini.example
Normal file
@@ -0,0 +1,36 @@
|
||||
# KX-Bridge Konfigurationsdatei
|
||||
# Kopiere diese Datei nach config.ini und trage deine Werte ein:
|
||||
# cp config.ini.example config.ini
|
||||
#
|
||||
# Credentials automatisch eintragen:
|
||||
# python3 tools/fetch_credentials.py --ip 192.168.x.x --write-config
|
||||
# Alternativ (Windows, ohne Drucker-IP bekannt):
|
||||
# extract_credentials.exe --write-env (liest aus laufendem AnycubicSlicerNext)
|
||||
|
||||
[connection]
|
||||
# IP-Adresse des Druckers im lokalen Netzwerk
|
||||
printer_ip = 192.168.x.x
|
||||
|
||||
# MQTT-Port (Anycubic Kobra X Standard: 9883)
|
||||
mqtt_port = 9883
|
||||
|
||||
# MQTT-Zugangsdaten (druckerspezifisch, beginnt mit "user")
|
||||
username = userXXXXXXXXXX
|
||||
password = XXXXXXXXXXXXXXX
|
||||
|
||||
# Geräte-ID (32-stelliger Hex-String, druckerspezifisch)
|
||||
device_id = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
||||
# Modell-ID (Kobra X Standard: 20030)
|
||||
mode_id = 20030
|
||||
|
||||
[print]
|
||||
# Standard-AMS-Slot für Einfarbdruck (auto = alle belegten Slots, 0-3 = fixer Slot)
|
||||
default_ams_slot = auto
|
||||
|
||||
# Auto-Leveling vor jedem Druck (1 = an, 0 = aus)
|
||||
auto_leveling = 1
|
||||
|
||||
[bridge]
|
||||
# Poll-Intervall in Sekunden
|
||||
poll_interval = 3
|
||||
@@ -57,8 +57,9 @@ def _load_config_file(path: pathlib.Path):
|
||||
"MQTT_PASSWORD": (CONFIG_SECTION_CONNECTION, "password"),
|
||||
"MODE_ID": (CONFIG_SECTION_CONNECTION, "mode_id"),
|
||||
"DEVICE_ID": (CONFIG_SECTION_CONNECTION, "device_id"),
|
||||
"DEFAULT_AMS_SLOT": (CONFIG_SECTION_PRINT, "default_ams_slot"),
|
||||
"AUTO_LEVELING": (CONFIG_SECTION_PRINT, "auto_leveling"),
|
||||
"DEFAULT_AMS_SLOT": (CONFIG_SECTION_PRINT, "default_ams_slot"),
|
||||
"AUTO_LEVELING": (CONFIG_SECTION_PRINT, "auto_leveling"),
|
||||
"BRIDGE_PRINTER_NAME": (CONFIG_SECTION_BRIDGE, "printer_name"),
|
||||
}
|
||||
for env_key, (section, option) in mapping.items():
|
||||
if env_key not in os.environ:
|
||||
@@ -128,6 +129,39 @@ elif _env_path:
|
||||
_config_path = _target
|
||||
|
||||
|
||||
def list_printers() -> list[dict]:
|
||||
"""Liest alle [printer_N]-Sektionen aus config.ini.
|
||||
|
||||
Jede Sektion kann folgende Keys haben:
|
||||
name, printer_ip, mqtt_port, username, password, mode_id, device_id,
|
||||
bridge_url, default_ams_slot, auto_leveling
|
||||
|
||||
Gibt eine leere Liste zurück wenn keine [printer_N]-Sektionen vorhanden sind
|
||||
(Single-Printer-Betrieb via [connection]).
|
||||
"""
|
||||
path = _find_config_file()
|
||||
if not path:
|
||||
return []
|
||||
cfg = configparser.ConfigParser()
|
||||
cfg.read(path, encoding="utf-8")
|
||||
printers: list[dict] = []
|
||||
idx = 1
|
||||
while True:
|
||||
section = f"printer_{idx}"
|
||||
if not cfg.has_section(section):
|
||||
break
|
||||
p = dict(cfg[section])
|
||||
p.setdefault("id", str(idx))
|
||||
if "mqtt_port" in p:
|
||||
try:
|
||||
p["mqtt_port"] = int(p["mqtt_port"])
|
||||
except ValueError:
|
||||
p["mqtt_port"] = 9883
|
||||
printers.append(p)
|
||||
idx += 1
|
||||
return printers
|
||||
|
||||
|
||||
def get(key: str, default: str = "") -> str:
|
||||
return os.environ.get(key, default)
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ services:
|
||||
build: .
|
||||
volumes:
|
||||
- ./config:/app/config
|
||||
- ./data:/app/data
|
||||
- ./.env:/app/.env:ro
|
||||
ports:
|
||||
- "7125:7125"
|
||||
- "7125-7130:7125-7130"
|
||||
restart: unless-stopped
|
||||
logging:
|
||||
driver: json-file
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user