From 710c4831c21f8ef7ca303b955ab8b95f93f68b94 Mon Sep 17 00:00:00 2001 From: viewit Date: Tue, 23 Jun 2026 14:55:03 +0200 Subject: [PATCH 01/41] feat: Nightly-Build-Support (docker-compose.nightly.yml + README) --- README.de.md | 16 ++++++++++++++++ README.md | 16 ++++++++++++++++ docker-compose.nightly.yml | 3 +++ 3 files changed, 35 insertions(+) create mode 100644 docker-compose.nightly.yml diff --git a/README.de.md b/README.de.md index e6931e4..98f77db 100644 --- a/README.de.md +++ b/README.de.md @@ -182,6 +182,22 @@ docker compose pull && docker compose up -d # auf neueste veröffentlichte Ver docker compose up -d --build # lokal selber bauen (statt zu pullen) ``` +### 🌙 Nightly-Builds + +Nightly-Builds enthalten die neuesten unveröffentlichten Funktionen und werden bei jedem Entwicklungs-Commit gebaut. +Sie können instabil sein — für Tests oder frühzeitigen Zugang zu neuen Features 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 diff --git a/README.md b/README.md index 7bf3199..315c658 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,22 @@ docker compose pull && docker compose up -d # update to the latest published i docker compose up -d --build # rebuild locally (instead of pulling) ``` +### 🌙 Nightly Builds + +Nightly builds contain the latest unreleased features and are built on every development commit. +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 diff --git a/docker-compose.nightly.yml b/docker-compose.nightly.yml new file mode 100644 index 0000000..af0c245 --- /dev/null +++ b/docker-compose.nightly.yml @@ -0,0 +1,3 @@ +services: + kx-bridge: + image: gitea.it-drui.de/viewit/kx-bridge:nightly From d20308cf2cdeb52d9ca8be60cd053bf34c9ed768 Mon Sep 17 00:00:00 2001 From: viewit Date: Tue, 23 Jun 2026 15:05:40 +0200 Subject: [PATCH 02/41] nightly: 0.9.27-nightly1 --- README.de.md | 16 --- README.md | 16 --- VERSION | 2 +- config_loader.py | 5 + kobrax_moonraker_bridge.py | 216 +++++++++++++++++++++++++++++++++- web/themes/default/app.js | 70 +++++++++++ web/themes/default/index.html | 7 ++ 7 files changed, 295 insertions(+), 37 deletions(-) diff --git a/README.de.md b/README.de.md index 98f77db..e6931e4 100644 --- a/README.de.md +++ b/README.de.md @@ -182,22 +182,6 @@ docker compose pull && docker compose up -d # auf neueste veröffentlichte Ver docker compose up -d --build # lokal selber bauen (statt zu pullen) ``` -### 🌙 Nightly-Builds - -Nightly-Builds enthalten die neuesten unveröffentlichten Funktionen und werden bei jedem Entwicklungs-Commit gebaut. -Sie können instabil sein — für Tests oder frühzeitigen Zugang zu neuen Features 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 diff --git a/README.md b/README.md index 315c658..7bf3199 100644 --- a/README.md +++ b/README.md @@ -180,22 +180,6 @@ docker compose pull && docker compose up -d # update to the latest published i docker compose up -d --build # rebuild locally (instead of pulling) ``` -### 🌙 Nightly Builds - -Nightly builds contain the latest unreleased features and are built on every development commit. -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 diff --git a/VERSION b/VERSION index 46e7a71..26c2639 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.26 +0.9.27-nightly1 diff --git a/config_loader.py b/config_loader.py index b1012e1..bf41b85 100644 --- a/config_loader.py +++ b/config_loader.py @@ -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: @@ -63,6 +64,8 @@ def _load_config_file(path: pathlib.Path): "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: @@ -317,3 +320,5 @@ 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")) diff --git a/kobrax_moonraker_bridge.py b/kobrax_moonraker_bridge.py index 930c34f..358a99a 100644 --- a/kobrax_moonraker_bridge.py +++ b/kobrax_moonraker_bridge.py @@ -733,6 +733,40 @@ class CameraCache: await asyncio.sleep(2.0) +class SpoolmanClient: + """Thin synchronous HTTP client for Spoolman filament tracking. + + Designed to be called from daemon threads (poll loop, _on_print callbacks). + Uses requests (already in requirements) so no event-loop dependency. + """ + + def __init__(self, server_url: str, sync_rate: int = 0): + self.server_url = server_url.rstrip("/") + self.sync_rate = sync_rate + + def _req(self, method: str, path: str, **kwargs): + import requests + r = requests.request(method, f"{self.server_url}{path}", timeout=5, **kwargs) + r.raise_for_status() + return r.json() + + def health_check(self) -> bool: + try: + self._req("GET", "/api/v1/health") + return True + except Exception: + return False + + def list_spools(self) -> list: + return self._req("GET", "/api/v1/spool") + + def use_filament(self, spool_id: int, use_length_mm: float) -> None: + """Report consumed filament length in mm. Spoolman converts to weight + using the spool's filament profile density.""" + self._req("PUT", f"/api/v1/spool/{spool_id}/use", + json={"use_length": round(use_length_mm, 2)}) + + class KobraXBridge: def __init__(self, client: KobraXClient, args=None, store=None, printer_id: str = "1", all_bridges=None): self.client = client @@ -793,6 +827,7 @@ class KobraXBridge: "file_ready": "", "print_start_dialog": getattr(args, "print_start_dialog", 1), "filament_mode": "toolhead", + "supplies_usage": 0, "ace_drying": {"status": 0, "target_temp": 0, "duration": 0, "remain_time": 0, "humidity": None, "current_temp": None}, } self._ams_slots: list[dict] = [] # flat global list; each entry has global_index + box_id @@ -819,6 +854,18 @@ class KobraXBridge: self._pending_preprint_skip: list[str] = [] self._pending_preprint_skip_deadline: float = 0.0 + # Spoolman filament tracking + _sm_url = (getattr(args, "spoolman_server", "") or "").strip() + self._spoolman: SpoolmanClient | None = ( + SpoolmanClient(_sm_url, getattr(args, "spoolman_sync_rate", 0)) + if _sm_url else None + ) + self._spoolman_slot_spools: dict[int, int] = {} # {ams_slot_idx: spoolman_spool_id} + self._spoolman_slot_usage: dict[int, float] = {} # per-slot accumulated mm this print + self._spoolman_slot_reported: dict[int, float] = {} # per-slot mm already sent to Spoolman + self._spoolman_last_usage: float = 0.0 # supplies_usage at last attribution tick + self._spoolman_last_sync: float = 0.0 + # Theme-Name prüfen (keine Sonderzeichen oder Umlaute) raw_theme = (getattr(args, "ui_theme", None) or "default").strip() if not _UI_THEME_NAME_RE.match(raw_theme): @@ -837,6 +884,139 @@ class KobraXBridge: client.callbacks["light/report"] = self._on_light client.callbacks["skip/report"] = self._on_skip + if self._spoolman: + threading.Thread( + target=lambda: log.info( + f"Spoolman: {'OK' if self._spoolman.health_check() else 'unreachable'} " + f"at {self._spoolman.server_url}" + ), + daemon=True, name="spoolman-health", + ).start() + + # ── Spoolman helpers ────────────────────────────────────────────────────── + + def _spoolman_filament_mm(self) -> float: + """Total filament_used_mm for the current print file from the GCode DB.""" + filename = self._state.get("filename", "") + if not filename: + return 0.0 + try: + gf = self._store.get_file_by_name(filename) + return float(gf.get("filament_used_mm") or 0.0) if gf else 0.0 + except Exception: + return 0.0 + + def _spoolman_attribute_tick(self, activity_map: dict) -> None: + """Attribute the supplies_usage delta since last tick to the active slot. + + Skips attribution during loading/unloading transitions (tool changes + + purges) to avoid charging the wrong spool for purge material.""" + if not self._spoolman or not self._spoolman_slot_spools: + return + if self._state.get("print_state") != "printing": + return + current = self._state.get("supplies_usage", 0) + delta = current - self._spoolman_last_usage + self._spoolman_last_usage = current + if delta <= 0: + return + loaded = self._ams_loaded_slot + if loaded < 0: + return + if activity_map.get(loaded): + return + self._spoolman_slot_usage[loaded] = self._spoolman_slot_usage.get(loaded, 0.0) + delta + + def _spoolman_unreported(self) -> dict[int, float]: + """Return {slot_idx: mm} of usage not yet reported to Spoolman. + + Falls back to equal split of total supplies_usage when per-slot + attribution data is absent (e.g. single-extruder with no AMS).""" + total_used = self._state.get("supplies_usage", 0) + if self._spoolman_slot_usage: + return { + slot: self._spoolman_slot_usage.get(slot, 0.0) + - self._spoolman_slot_reported.get(slot, 0.0) + for slot in self._spoolman_slot_spools + } + n = len(self._spoolman_slot_spools) + already = sum(self._spoolman_slot_reported.values()) + per = (total_used - already) / n if n else 0.0 + return {slot: per for slot in self._spoolman_slot_spools} + + def _spoolman_report(self, unreported: dict[int, float], min_mm: float = 0.1) -> None: + """Fire-and-forget report of unreported mm to each mapped spool.""" + sm = self._spoolman + for slot_idx, mm in unreported.items(): + if mm < min_mm: + continue + spool_id = self._spoolman_slot_spools.get(slot_idx) + if not spool_id: + continue + self._spoolman_slot_reported[slot_idx] = ( + self._spoolman_slot_reported.get(slot_idx, 0.0) + mm + ) + def _send(sid=spool_id, length=mm): + try: + sm.use_filament(sid, length) + log.info(f"Spoolman: {length:.1f} mm → spool {sid}") + except Exception as e: + log.warning(f"Spoolman: report failed (spool {sid}): {e}") + threading.Thread(target=_send, daemon=True, name="spoolman-report").start() + + def _spoolman_notify_end(self): + """Report remaining filament on print end.""" + if not self._spoolman or not self._spoolman_slot_spools: + return + self._spoolman_report(self._spoolman_unreported()) + + def _spoolman_sync_midprint(self): + """Report incremental filament usage during a print (sync_rate interval).""" + if not self._spoolman or not self._spoolman_slot_spools: + return + self._spoolman_report(self._spoolman_unreported(), min_mm=10.0) + + # ── Spoolman API handlers ───────────────────────────────────────────────── + + async def handle_kx_spoolman_status(self, request): + """GET /kx/spoolman/status""" + return self._json_cors({ + "configured": bool(self._spoolman), + "server": self._spoolman.server_url if self._spoolman else "", + "sync_rate": self._spoolman.sync_rate if self._spoolman else 0, + "slot_spools": {str(k): v for k, v in self._spoolman_slot_spools.items()}, + }) + + async def handle_kx_spoolman_spools(self, request): + """GET /kx/spoolman/spools — proxied from Spoolman.""" + if not self._spoolman: + return self._json_cors({"error": "Spoolman not configured"}, status=503) + try: + spools = await asyncio.get_event_loop().run_in_executor( + None, self._spoolman.list_spools + ) + return self._json_cors({"spools": spools}) + except Exception as e: + log.warning(f"Spoolman: list_spools failed: {e}") + return self._json_cors({"error": str(e)}, status=502) + + async def handle_kx_spoolman_set_active(self, request): + """POST /kx/spoolman/active-spool + Body: {"slot_map": {"0": 42, "2": 17}} — AMS slot index → Spoolman spool ID.""" + try: + data = await request.json() + except Exception: + return self._json_cors({"error": "invalid JSON"}, status=400) + slot_map = data.get("slot_map") or {} + self._spoolman_slot_spools = { + int(k): int(v) for k, v in slot_map.items() + if str(v).isdigit() and int(v) > 0 + } + self._spoolman_slot_usage = {} + self._spoolman_slot_reported = {} + self._spoolman_last_usage = 0.0 + return self._json_cors({"slot_spools": {str(k): v for k, v in self._spoolman_slot_spools.items()}}) + def _default_ace_dry_presets(self) -> dict[str, dict]: return { "pla": {"temp": 45, "duration_sec": 4 * 3600}, @@ -951,15 +1131,21 @@ class KobraXBridge: printer_id=self._printer_id, ) log.info(f"Job started: {self._current_job_id} for {filename}") + self._spoolman_slot_usage = {} + self._spoolman_slot_reported = {} + self._spoolman_last_usage = 0.0 + self._spoolman_last_sync = 0.0 # Job-History: Druckende erkennen if kobra_state in ("finished",) and self._current_job_id: self._store.finish_job(self._current_job_id, status="completed") log.info(f"Job abgeschlossen: {self._current_job_id}") + self._spoolman_notify_end() self._current_job_id = "" elif kobra_state in ("stoped", "canceled") and self._current_job_id: self._store.finish_job(self._current_job_id, status="cancelled") log.info(f"Job abgebrochen: {self._current_job_id}") + self._spoolman_notify_end() self._current_job_id = "" # Nach Druckende das Upload-Banner verschwinden lassen (Issue #29): der @@ -976,6 +1162,7 @@ class KobraXBridge: self._state["slicer_time"] = 0 self._state["layer_height"] = 0.0 self._state["first_layer_height"] = 0.0 + self._state["supplies_usage"] = 0 self._thumbnail_b64 = "" self._state["filename"] = d.get("filename", self._state["filename"]) if "progress" in d: @@ -990,6 +1177,8 @@ class KobraXBridge: self._state["total_layers"] = d["total_layers"] if "taskid" in d: self._state["taskid"] = str(d["taskid"]) + if "supplies_usage" in d: + self._state["supplies_usage"] = int(d["supplies_usage"]) settings = d.get("settings") or {} if "print_speed_mode" in settings: self._state["print_speed_mode"] = int(settings["print_speed_mode"]) @@ -2967,10 +3156,10 @@ class KobraXBridge: if not file_data: return web.json_response({"error": "no file received"}, status=400) - # Nur druckbare Dateien zulassen (Issue #59) — verhindert dass z.B. ein - # JPG im File-Browser landet. OrcaSlicer-Uploads sind .gcode/.gcode.3mf, - # der Kobra X akzeptiert .gcode/.3mf/.bgcode. - _allowed_ext = (".gcode", ".gcode.3mf", ".3mf", ".bgcode") + # Nur druckbare Dateien zulassen (Issue #59) — der Kobra X akzeptiert + # ausschließlich .gcode und .bgcode; .3mf-Uploads werden vom Drucker + # nicht verarbeitet und daher abgelehnt (Issue #59, @gangoke). + _allowed_ext = (".gcode", ".bgcode") _fn_lower = (remote_filename or "").lower() if not _fn_lower.endswith(_allowed_ext): log.warning(f"Upload abgelehnt (kein GCode): {remote_filename}") @@ -4804,6 +4993,14 @@ class KobraXBridge: print_r = self.client.publish("print", "query", timeout=3.0) if print_r: self._on_print(print_r) + # Spoolman mid-print sync + if (self._spoolman and self._spoolman.sync_rate > 0 + and self._spoolman_slot_spools + and self._state.get("print_state") == "printing"): + now = time.time() + if now - self._spoolman_last_sync >= self._spoolman.sync_rate: + self._spoolman_sync_midprint() + self._spoolman_last_sync = now box = self.client.query_multicolor_box() if box: data = box.get("data") or {} @@ -4820,6 +5017,10 @@ class KobraXBridge: if global_slots: self._ams_slots = global_slots self._ams_loaded_slot = global_loaded + self._spoolman_attribute_tick(activity_map) + else: + # No multiColorBox data — still attribute (no transitions to skip) + self._spoolman_attribute_tick({}) except Exception as e: log.warning(f"Poll-Fehler: {e}") # Prüfen ob Drucker wirklich weg ist @@ -4954,6 +5155,9 @@ def build_app(bridge: KobraXBridge) -> web.Application: r.add_post("/kx/skip", bridge.handle_kx_skip) r.add_post("/kx/skip/query", bridge.handle_kx_skip_query) r.add_get("/kx/skip/state", bridge.handle_kx_skip_state) + r.add_get("/kx/spoolman/status", bridge.handle_kx_spoolman_status) + r.add_get("/kx/spoolman/spools", bridge.handle_kx_spoolman_spools) + r.add_post("/kx/spoolman/active-spool", bridge.handle_kx_spoolman_set_active) r.add_route("OPTIONS", "/kx/{path:.*}", bridge.handle_kx_options) # Root + Printer-Routen (Single-Page, JS liest Pathname) @@ -5119,6 +5323,10 @@ def main(): parser.add_argument("--web-upload-warning", type=int, default=env_loader.WEB_UPLOAD_WARNING) parser.add_argument("--print-start-dialog", dest="print_start_dialog", type=int, default=env_loader.PRINT_START_DIALOG) parser.add_argument("--file-ready-dialog", dest="print_start_dialog", type=int) + parser.add_argument("--spoolman-server", default=env_loader.SPOOLMAN_SERVER, + help="Spoolman URL (e.g. http://192.168.x.x:7912); leave empty to disable") + parser.add_argument("--spoolman-sync-rate", type=int, default=env_loader.SPOOLMAN_SYNC_RATE, + help="Mid-print filament sync interval in seconds (0 = only on print end)") parser.add_argument("--host", default="0.0.0.0", help="Bind-Adresse für den Bridge-Server") diff --git a/web/themes/default/app.js b/web/themes/default/app.js index 52bacf9..8d51f74 100644 --- a/web/themes/default/app.js +++ b/web/themes/default/app.js @@ -44,6 +44,62 @@ var ACE_DRY_PRESETS={ custom_3:{name:'Custom 3',temp:45,duration_sec:4*3600} }; +// Spoolman state +var _spoolmanStatus={configured:false,server:'',sync_rate:0,slot_spools:{}}; +var _spoolmanSpools=[]; +var _slotSpoolMap={}; // {String(global_index): spoolman_spool_id} — last committed assignment + +function _loadSpoolmanStatus(){ + fetch(_apiUrl('/kx/spoolman/status')).then(function(r){return r.json();}).then(function(d){ + _spoolmanStatus=d; + _slotSpoolMap=d.slot_spools||{}; + }).catch(function(){}); +} + +function _buildSpoolmanSection(){ + var sec=document.getElementById('fd-spoolman-section'); + var rows=document.getElementById('fd-spoolman-rows'); + var loading=document.getElementById('fd-spoolman-loading'); + if(!sec||!rows)return; + if(!_spoolmanStatus.configured){sec.style.display='none';return;} + sec.style.display=''; + rows.innerHTML=''; + if(loading)loading.style.display=''; + + var usedSlots={}; + document.querySelectorAll('#fd-slots select').forEach(function(sel){ + var idx=parseInt(sel.value); + if(idx>=0){ + var slot=(_amsSlots||[]).find(function(s){return s.slot_index===idx;}); + if(slot&&!usedSlots[idx])usedSlots[idx]=slot; + } + }); + + fetch(_apiUrl('/kx/spoolman/spools')).then(function(r){return r.json();}).then(function(d){ + if(loading)loading.style.display='none'; + _spoolmanSpools=d.spools||[]; + var slotKeys=Object.keys(usedSlots).map(Number).sort(function(a,b){return a-b;}); + if(!slotKeys.length){rows.innerHTML='';return;} + rows.innerHTML=slotKeys.map(function(idx){ + var slot=usedSlots[idx]; + var col=(slot.color_hex||'#888'); + var currentSpool=_slotSpoolMap[String(idx)]||''; + var opts=''+_spoolmanSpools.map(function(sp){ + var rem=sp.remaining_weight!=null?' ('+sp.remaining_weight.toFixed(0)+'g)':''; + var vendor=sp.filament&&sp.filament.vendor?sp.filament.vendor+' ':''; + var name=sp.filament&&sp.filament.name?sp.filament.name:'Spool'; + return ''; + }).join(''); + return '
'+ + ''+ + 'Slot '+(idx+1)+''+ + '
'; + }).join(''); + }).catch(function(){if(loading)loading.style.display='none';}); +} + function _aceAutoRefillGet(aceId){return !!aceAutoRefillPrefs[String(aceId)];} function _aceAutoRefillSet(aceId,on){ aceAutoRefillPrefs[String(aceId)]=!!on; @@ -2494,6 +2550,7 @@ function openFilamentDialog(slots){ _amsSlots=slots .filter(function(s){return s.status==='loaded';}) .sort(function(a,b){return (a.slot_index||0)-(b.slot_index||0);}); + _loadSpoolmanStatus(); var dlg=document.getElementById('filament-dialog'); var title=document.getElementById('fd-title'); var body=document.getElementById('fd-slots'); @@ -2623,6 +2680,8 @@ function openFilamentDialog(slots){ }).join(''); } if(dlg)dlg.classList.add('open'); + // Spoolman-Section nach Slot-Render aufbauen (braucht die selects im DOM) + setTimeout(_buildSpoolmanSection, 0); } function closeFilamentDialog(){ @@ -2675,6 +2734,17 @@ function confirmFilamentPrint(){ var excludedObjects=_printObjects.filter(function(o){return o.skip;}).map(function(o){return o.name;}); var fdAlEl=document.getElementById('fd-auto-leveling'); var fdAutoLeveling=fdAlEl?( fdAlEl.checked?1:0):(S.auto_leveling===undefined?1:S.auto_leveling?1:0); + // Spoolman: Slot→Spool-Mapping aus Dialog sammeln und senden + if(_spoolmanStatus.configured){ + var slotMap={}; + document.querySelectorAll('[data-spool-slot]').forEach(function(sel){ + var idx=sel.dataset.spoolSlot; + var val=parseInt(sel.value); + if(val>0)slotMap[idx]=val; + }); + _slotSpoolMap=slotMap; + post('/kx/spoolman/active-spool',{slot_map:slotMap}).catch(function(){}); + } closeFilamentDialog(); if(_filamentDialogMode==='banner'){ // Banner-Modus: /kx/print bevorzugen wenn _storeFileId bekannt (gleicher Pfad wie File-Browser). diff --git a/web/themes/default/index.html b/web/themes/default/index.html index 843d553..d677621 100644 --- a/web/themes/default/index.html +++ b/web/themes/default/index.html @@ -646,6 +646,13 @@ +
From 2b39cc1a7874181d2f879e7ce770f847f4ac3115 Mon Sep 17 00:00:00 2001 From: viewit Date: Tue, 23 Jun 2026 15:15:20 +0200 Subject: [PATCH 03/41] feat: Portainer-Nightly-Stack (docker-compose.portainer-nightly.yml) --- docker-compose.portainer-nightly.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docker-compose.portainer-nightly.yml diff --git a/docker-compose.portainer-nightly.yml b/docker-compose.portainer-nightly.yml new file mode 100644 index 0000000..6aaf2bb --- /dev/null +++ b/docker-compose.portainer-nightly.yml @@ -0,0 +1,26 @@ +# 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: + - 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: From 3595cf839cba114a8a5b0ee9195d1990a78071d3 Mon Sep 17 00:00:00 2001 From: viewit Date: Tue, 23 Jun 2026 15:18:18 +0200 Subject: [PATCH 04/41] fix: Bind-Mounts auf /mnt/dockerdata/kx-nightly --- docker-compose.portainer-nightly.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docker-compose.portainer-nightly.yml b/docker-compose.portainer-nightly.yml index 6aaf2bb..3e043f1 100644 --- a/docker-compose.portainer-nightly.yml +++ b/docker-compose.portainer-nightly.yml @@ -9,8 +9,8 @@ services: kx-bridge: image: gitea.it-drui.de/viewit/kx-bridge:nightly volumes: - - kx-bridge-config:/app/config - - kx-bridge-data:/app/data + - /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" @@ -21,6 +21,5 @@ services: max-size: "10m" max-file: "3" -volumes: - kx-bridge-config: - kx-bridge-data: +# Verzeichnisse müssen auf dem Host existieren: +# mkdir -p /mnt/dockerdata/kx-nightly/config /mnt/dockerdata/kx-nightly/data From 72f77d92af79a12712a6c854e78ed1190064b3d5 Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 12:58:46 +0200 Subject: [PATCH 05/41] feat: Integrationen-Tab + KobraX Full Stack Compose + CI-Workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Settings-Tab "Integrationen": Spoolman URL/Sync-Rate konfigurierbar, Obico Read-only Hinweis auf moonraker-obico.cfg - docker-compose-KX.yml: Portainer-kompatibler Full Stack (KX-Bridge + Obico Self-Hosted + Spoolman + moonraker-obico Plugin) - moonraker-obico.cfg.example: Verbindungsvorlage für Obico-Integration - .gitea/workflows/docker-publish.yml: Push auf nightly → :nightly Image, v*-Tag → :latest + : --- .gitea/workflows/docker-publish.yml | 56 ++++++++ docker-compose-KX.yml | 210 ++++++++++++++++++++++++++++ kobrax_moonraker_bridge.py | 14 +- moonraker-obico.cfg.example | 18 +++ web/themes/default/app.js | 22 +++ web/themes/default/index.html | 28 ++++ web/translations/de.json | 9 +- web/translations/en.json | 9 +- web/translations/es.json | 9 +- web/translations/fr.json | 9 +- web/translations/it.json | 7 +- web/translations/zh-cn.json | 9 +- 12 files changed, 388 insertions(+), 12 deletions(-) create mode 100644 .gitea/workflows/docker-publish.yml create mode 100644 docker-compose-KX.yml create mode 100644 moonraker-obico.cfg.example diff --git a/.gitea/workflows/docker-publish.yml b/.gitea/workflows/docker-publish.yml new file mode 100644 index 0000000..98589d5 --- /dev/null +++ b/.gitea/workflows/docker-publish.yml @@ -0,0 +1,56 @@ +name: Docker Publish + +on: + push: + tags: + - 'v*' + branches: + - nightly + workflow_dispatch: + +jobs: + build-push: + runs-on: self-hosted + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + clean: true + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Gitea registry + uses: docker/login-action@v3 + with: + registry: gitea.it-drui.de + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Derive tags + id: meta + run: | + REF="${GITHUB_REF}" + if [[ "$REF" == refs/tags/v* ]]; then + VER=$(cat VERSION) + TAGS="gitea.it-drui.de/viewit/kx-bridge:latest" + TAGS="$TAGS,gitea.it-drui.de/viewit/kx-bridge:$VER" + else + # Push auf nightly-Branch oder workflow_dispatch + TAGS="gitea.it-drui.de/viewit/kx-bridge:nightly" + fi + echo "tags=$TAGS" >>"$GITHUB_OUTPUT" + echo "Pushing tags: $TAGS" + + - name: Build & push (amd64 + arm64) + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + provenance: false + no-cache: true + tags: ${{ steps.meta.outputs.tags }} diff --git a/docker-compose-KX.yml b/docker-compose-KX.yml new file mode 100644 index 0000000..12752d0 --- /dev/null +++ b/docker-compose-KX.yml @@ -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// +# +# 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=' + # → 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 diff --git a/kobrax_moonraker_bridge.py b/kobrax_moonraker_bridge.py index 358a99a..b70d1f4 100644 --- a/kobrax_moonraker_bridge.py +++ b/kobrax_moonraker_bridge.py @@ -4218,6 +4218,8 @@ class KobraXBridge: "filament_profiles": {str(k): v for k, v in self._filament_profiles.items()}, "visible_vendors": self._visible_vendors, "ace_dry_presets": self._ace_dry_presets, + "spoolman_server": getattr(self._args, "spoolman_server", "") or "", + "spoolman_sync_rate": getattr(self._args, "spoolman_sync_rate", 0), }) async def handle_api_settings_post(self, request): @@ -4232,7 +4234,7 @@ class KobraXBridge: cfg.read(config_path, encoding="utf-8") # Sections sicherstellen - for section in ("connection", "print", "bridge", "ace_dry_presets"): + for section in ("connection", "print", "bridge", "ace_dry_presets", "spoolman"): if not cfg.has_section(section): cfg.add_section(section) @@ -4262,6 +4264,16 @@ class KobraXBridge: elif cfg.has_option("bridge", "printer_name"): cfg.remove_option("bridge", "printer_name") + # Spoolman + if "spoolman_server" in data: + cfg.set("spoolman", "server", str(data["spoolman_server"]).strip()) + if "spoolman_sync_rate" in data: + try: + sr = max(0, int(data["spoolman_sync_rate"])) + except (TypeError, ValueError): + sr = 30 + cfg.set("spoolman", "sync_rate", str(sr)) + incoming_presets = data.get("ace_dry_presets") if isinstance(data, dict) else None presets = self._sanitize_ace_dry_presets(incoming_presets if isinstance(incoming_presets, dict) else self._ace_dry_presets) for key, val in presets.items(): diff --git a/moonraker-obico.cfg.example b/moonraker-obico.cfg.example new file mode 100644 index 0000000..e166677 --- /dev/null +++ b/moonraker-obico.cfg.example @@ -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 diff --git a/web/themes/default/app.js b/web/themes/default/app.js index 8d51f74..103b8db 100644 --- a/web/themes/default/app.js +++ b/web/themes/default/app.js @@ -53,8 +53,19 @@ function _loadSpoolmanStatus(){ fetch(_apiUrl('/kx/spoolman/status')).then(function(r){return r.json();}).then(function(d){ _spoolmanStatus=d; _slotSpoolMap=d.slot_spools||{}; + _updateSpoolmanStatusDot(); }).catch(function(){}); } +function _updateSpoolmanStatusDot(){ + var dot=document.getElementById('spoolman-status-dot'); + var lbl=document.getElementById('spoolman-status-lbl'); + if(!dot||!lbl)return; + if(_spoolmanStatus.configured){ + dot.style.color='var(--ok)';lbl.textContent=_spoolmanStatus.server||'verbunden'; + } else { + dot.style.color='var(--txt2)';lbl.textContent='nicht konfiguriert'; + } +} function _buildSpoolmanSection(){ var sec=document.getElementById('fd-spoolman-section'); @@ -385,6 +396,11 @@ function applyLang(){ setText('setcat-lbl-display',T.settings_cat_display||'Darstellung'); setText('setcat-lbl-display2',T.settings_cat_display||'Darstellung'); setText('setcat-lbl-filament',T.settings_cat_filament||'Filament'); + setText('setcat-lbl-integrations',T.settings_integrations||'Integrationen'); + setText('modal-sec-spoolman',T.modal_sec_spoolman||'Spoolman'); + setText('lbl-spoolman-url',T.lbl_spoolman_url||'Server-URL'); + setText('lbl-spoolman-sync-rate',T.lbl_spoolman_sync_rate||'Sync-Rate (s, 0=aus)'); + setText('modal-sec-obico',T.modal_sec_obico||'Obico'); setText('setcat-lbl-system',T.settings_version||'System'); setText('lbl-set-lang',T.settings_cat_language||'Sprache'); setText('lbl-set-theme',T.settings_cat_theme||'Hell / Dunkel umschalten'); @@ -1058,6 +1074,10 @@ function openSettings(){ pi.value=sec; } renderFilamentMapping(d.filament_profiles||{}); + // Spoolman + var su=document.getElementById('s-spoolman-url');if(su)su.value=d.spoolman_server||''; + var sr=document.getElementById('s-spoolman-sync-rate');if(sr)sr.value=(d.spoolman_sync_rate!==undefined?d.spoolman_sync_rate:30); + _updateSpoolmanStatusDot(); }); // Sprach-Select im Settings-Panel mit aktueller Sprache spiegeln var ls=document.getElementById('s-lang-select'); @@ -1626,6 +1646,8 @@ function saveSettings(){ print_start_dialog: parseInt((document.getElementById('s-file-ready-mode')||{}).value||'1',10), web_upload_warning:webUploadWarning, poll_interval: Math.min(60,Math.max(1,parseInt((document.getElementById('s-poll-interval')||{}).value,10)||3)), + spoolman_server: (document.getElementById('s-spoolman-url')||{}).value||'', + spoolman_sync_rate: Math.max(0,parseInt((document.getElementById('s-spoolman-sync-rate')||{}).value||'30',10)), }).then(function(){ btn.textContent=T.update_restarting; setTimeout(function(){ diff --git a/web/themes/default/index.html b/web/themes/default/index.html index d677621..7462845 100644 --- a/web/themes/default/index.html +++ b/web/themes/default/index.html @@ -435,6 +435,7 @@ +
@@ -570,6 +571,33 @@ + +
+ +
+
🧵 Spoolman
+
+ + +
+
+ + +
+
+ +
+
+ +
+
🕵 Obico
+
+ Obico wird über den moonraker-obico-Container konfiguriert.
+ Config-Datei: /mnt/dockerdata/KobraXStack/moonraker-obico/moonraker-obico.cfg +
+
+
+
diff --git a/web/translations/de.json b/web/translations/de.json index c5011df..35ecc76 100644 --- a/web/translations/de.json +++ b/web/translations/de.json @@ -287,5 +287,10 @@ "log_clear": "✕ Leeren", "log_filter_placeholder": "Filtern…", "skip_cancel": "Abbrechen", - "skip_confirm": "Überspringen" -} + "skip_confirm": "Überspringen", + "settings_integrations": "Integrationen", + "modal_sec_spoolman": "Spoolman", + "lbl_spoolman_url": "Server-URL", + "lbl_spoolman_sync_rate": "Sync-Rate (s, 0=aus)", + "modal_sec_obico": "Obico" +} \ No newline at end of file diff --git a/web/translations/en.json b/web/translations/en.json index 61c5c86..7359609 100644 --- a/web/translations/en.json +++ b/web/translations/en.json @@ -287,5 +287,10 @@ "sf_new": "New", "ss_date": "↓ Date", "ss_name": "A–Z Name", - "ss_dur": "⏱ Print time" -} + "ss_dur": "⏱ Print time", + "settings_integrations": "Integrations", + "modal_sec_spoolman": "Spoolman", + "lbl_spoolman_url": "Server URL", + "lbl_spoolman_sync_rate": "Sync rate (s, 0=off)", + "modal_sec_obico": "Obico" +} \ No newline at end of file diff --git a/web/translations/es.json b/web/translations/es.json index 822cf4b..dfca2a5 100644 --- a/web/translations/es.json +++ b/web/translations/es.json @@ -287,5 +287,10 @@ "log_clear": "✕ Limpiar", "log_filter_placeholder": "Filtrar…", "skip_cancel": "Cancelar", - "skip_confirm": "Omitir" -} + "skip_confirm": "Omitir", + "settings_integrations": "Integraciones", + "modal_sec_spoolman": "Spoolman", + "lbl_spoolman_url": "URL del servidor", + "lbl_spoolman_sync_rate": "Tasa de sincronización (s, 0=desact.)", + "modal_sec_obico": "Obico" +} \ No newline at end of file diff --git a/web/translations/fr.json b/web/translations/fr.json index 01fc6f6..bf4f0ba 100644 --- a/web/translations/fr.json +++ b/web/translations/fr.json @@ -287,5 +287,10 @@ "log_clear": "✕ Effacer", "log_filter_placeholder": "Filtrer…", "skip_cancel": "Annuler", - "skip_confirm": "Ignorer" -} + "skip_confirm": "Ignorer", + "settings_integrations": "Intégrations", + "modal_sec_spoolman": "Spoolman", + "lbl_spoolman_url": "URL du serveur", + "lbl_spoolman_sync_rate": "Taux de sync. (s, 0=désact.)", + "modal_sec_obico": "Obico" +} \ No newline at end of file diff --git a/web/translations/it.json b/web/translations/it.json index 56ed376..4a9b504 100644 --- a/web/translations/it.json +++ b/web/translations/it.json @@ -287,5 +287,10 @@ "sf_new": "Nuovo", "ss_date": "↓ Data", "ss_name": "Nome A–Z", - "ss_dur": "⏱ Tempo di stampa" + "ss_dur": "⏱ Tempo di stampa", + "settings_integrations": "Integrazioni", + "modal_sec_spoolman": "Spoolman", + "lbl_spoolman_url": "URL server", + "lbl_spoolman_sync_rate": "Frequenza sync (s, 0=disatt.)", + "modal_sec_obico": "Obico" } \ No newline at end of file diff --git a/web/translations/zh-cn.json b/web/translations/zh-cn.json index 8225ee0..d376c21 100644 --- a/web/translations/zh-cn.json +++ b/web/translations/zh-cn.json @@ -287,5 +287,10 @@ "log_clear": "✕ 清空", "log_filter_placeholder": "筛选…", "skip_cancel": "取消", - "skip_confirm": "跳过" -} + "skip_confirm": "跳过", + "settings_integrations": "集成", + "modal_sec_spoolman": "Spoolman", + "lbl_spoolman_url": "服务器地址", + "lbl_spoolman_sync_rate": "同步频率(秒,0=关闭)", + "modal_sec_obico": "Obico" +} \ No newline at end of file From 282c02ae0a3be96040a431aba0876ef47c4ca5f1 Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 13:01:46 +0200 Subject: [PATCH 06/41] =?UTF-8?q?chore:=20.runner-token=20zu=20.gitignore?= =?UTF-8?q?=20hinzuf=C3=BCgen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 167af95..c8dfb6e 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ config/*.ini data/ !data/orca_filaments.json +.runner-token From fa8e0c149116cb4480a7b05dfb049c44e34122e2 Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 13:08:49 +0200 Subject: [PATCH 07/41] ci: test workflow trigger auf nightly-Branch From 6165a7f62a6c1b73a94b8184ea954df9481c7a0d Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 13:09:26 +0200 Subject: [PATCH 08/41] ci: retrigger workflow test From c9043e9630e3c1d51cf65b3c88a4c897dc640c18 Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 13:10:55 +0200 Subject: [PATCH 09/41] =?UTF-8?q?ci:=20nightly-Datum-Tag=20hinzuf=C3=BCgen?= =?UTF-8?q?=20(nightly-YYYYMMDD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/docker-publish.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/docker-publish.yml b/.gitea/workflows/docker-publish.yml index 98589d5..31119a3 100644 --- a/.gitea/workflows/docker-publish.yml +++ b/.gitea/workflows/docker-publish.yml @@ -35,12 +35,15 @@ jobs: run: | REF="${GITHUB_REF}" if [[ "$REF" == refs/tags/v* ]]; then + # Stable Release: :latest + : VER=$(cat VERSION) TAGS="gitea.it-drui.de/viewit/kx-bridge:latest" TAGS="$TAGS,gitea.it-drui.de/viewit/kx-bridge:$VER" else - # Push auf nightly-Branch oder workflow_dispatch + # Nightly-Branch-Push: :nightly + :nightly- + DATE=$(date +%Y%m%d) TAGS="gitea.it-drui.de/viewit/kx-bridge:nightly" + TAGS="$TAGS,gitea.it-drui.de/viewit/kx-bridge:nightly-$DATE" fi echo "tags=$TAGS" >>"$GITHUB_OUTPUT" echo "Pushing tags: $TAGS" From 877cddb1baddaf12c7a8a015328a8bc28839eb5b Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 13:21:29 +0200 Subject: [PATCH 10/41] =?UTF-8?q?chore:=20Repo-Struktur=20vollst=C3=A4ndig?= =?UTF-8?q?=20aufsetzen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Workflows: docker-publish.yml → nightly.yml + release.yml + pr-check.yml (nightly: Branch-Push + Cron 02:00, release: v*-Tag, pr-check: lint+tests) - Issue-Templates: bug_report.md + feature_request.md (englisch) - PR-Template: pull_request_template.md (englisch) - Claude-Agenten: reviewer, changelog, test-writer, nightly-prep, docker-check, moonraker-debug + settings.json - agents.md: Agenten-Übersicht im Repo-Root - .gitignore: .runner-token + secrets/ ausgeschlossen --- .claude/agents/changelog.md | 31 +++++++++++++ .claude/agents/docker-check.md | 32 +++++++++++++ .claude/agents/moonraker-debug.md | 23 +++++++++ .claude/agents/nightly-prep.md | 25 ++++++++++ .claude/agents/reviewer.md | 24 ++++++++++ .claude/agents/test-writer.md | 26 +++++++++++ .claude/settings.json | 12 +++++ .gitea/ISSUE_TEMPLATE/bug_report.md | 29 ++++++++++++ .gitea/ISSUE_TEMPLATE/feature_request.md | 14 ++++++ .gitea/pull_request_template.md | 21 +++++++++ .gitea/workflows/docker-publish.yml | 59 ------------------------ .gitea/workflows/nightly.yml | 55 ++++++++++++++++++++++ .gitea/workflows/pr-check.yml | 31 +++++++++++++ .gitea/workflows/release.yml | 58 +++++++++++++++++++++++ .gitignore | 4 ++ agents.md | 31 +++++++++++++ 16 files changed, 416 insertions(+), 59 deletions(-) create mode 100644 .claude/agents/changelog.md create mode 100644 .claude/agents/docker-check.md create mode 100644 .claude/agents/moonraker-debug.md create mode 100644 .claude/agents/nightly-prep.md create mode 100644 .claude/agents/reviewer.md create mode 100644 .claude/agents/test-writer.md create mode 100644 .claude/settings.json create mode 100644 .gitea/ISSUE_TEMPLATE/bug_report.md create mode 100644 .gitea/ISSUE_TEMPLATE/feature_request.md create mode 100644 .gitea/pull_request_template.md delete mode 100644 .gitea/workflows/docker-publish.yml create mode 100644 .gitea/workflows/nightly.yml create mode 100644 .gitea/workflows/pr-check.yml create mode 100644 .gitea/workflows/release.yml create mode 100644 agents.md diff --git a/.claude/agents/changelog.md b/.claude/agents/changelog.md new file mode 100644 index 0000000..d009f2f --- /dev/null +++ b/.claude/agents/changelog.md @@ -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. diff --git a/.claude/agents/docker-check.md b/.claude/agents/docker-check.md new file mode 100644 index 0000000..e889aa0 --- /dev/null +++ b/.claude/agents/docker-check.md @@ -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. diff --git a/.claude/agents/moonraker-debug.md b/.claude/agents/moonraker-debug.md new file mode 100644 index 0000000..5a9c400 --- /dev/null +++ b/.claude/agents/moonraker-debug.md @@ -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) diff --git a/.claude/agents/nightly-prep.md b/.claude/agents/nightly-prep.md new file mode 100644 index 0000000..9c3d656 --- /dev/null +++ b/.claude/agents/nightly-prep.md @@ -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] diff --git a/.claude/agents/reviewer.md b/.claude/agents/reviewer.md new file mode 100644 index 0000000..85117b7 --- /dev/null +++ b/.claude/agents/reviewer.md @@ -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 diff --git a/.claude/agents/test-writer.md b/.claude/agents/test-writer.md new file mode 100644 index 0000000..dc4a52d --- /dev/null +++ b/.claude/agents/test-writer.md @@ -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_.py` +Verwende pytest-Fixtures für Moonraker-Mock-Responses. +Keine echten Netzwerkaufrufe in Tests. diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..014fbd2 --- /dev/null +++ b/.claude/settings.json @@ -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 + } +} diff --git a/.gitea/ISSUE_TEMPLATE/bug_report.md b/.gitea/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..cba63ab --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug Report +about: Report a bug in KX-Bridge +labels: bug +--- + +## Description + + +## 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 +``` + +``` diff --git a/.gitea/ISSUE_TEMPLATE/feature_request.md b/.gitea/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..7261fec --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,14 @@ +--- +name: Feature Request +about: Suggest a new feature or improvement +labels: enhancement +--- + +## Description + + +## Motivation + + +## Proposed Implementation + diff --git a/.gitea/pull_request_template.md b/.gitea/pull_request_template.md new file mode 100644 index 0000000..74818f8 --- /dev/null +++ b/.gitea/pull_request_template.md @@ -0,0 +1,21 @@ +## Description + + +## 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 diff --git a/.gitea/workflows/docker-publish.yml b/.gitea/workflows/docker-publish.yml deleted file mode 100644 index 31119a3..0000000 --- a/.gitea/workflows/docker-publish.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Docker Publish - -on: - push: - tags: - - 'v*' - branches: - - nightly - workflow_dispatch: - -jobs: - build-push: - runs-on: self-hosted - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - clean: true - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to Gitea registry - uses: docker/login-action@v3 - with: - registry: gitea.it-drui.de - username: ${{ secrets.REGISTRY_USER }} - password: ${{ secrets.REGISTRY_TOKEN }} - - - name: Derive tags - id: meta - run: | - REF="${GITHUB_REF}" - if [[ "$REF" == refs/tags/v* ]]; then - # Stable Release: :latest + : - VER=$(cat VERSION) - TAGS="gitea.it-drui.de/viewit/kx-bridge:latest" - TAGS="$TAGS,gitea.it-drui.de/viewit/kx-bridge:$VER" - else - # Nightly-Branch-Push: :nightly + :nightly- - DATE=$(date +%Y%m%d) - TAGS="gitea.it-drui.de/viewit/kx-bridge:nightly" - TAGS="$TAGS,gitea.it-drui.de/viewit/kx-bridge:nightly-$DATE" - fi - echo "tags=$TAGS" >>"$GITHUB_OUTPUT" - echo "Pushing tags: $TAGS" - - - name: Build & push (amd64 + arm64) - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64,linux/arm64 - push: true - provenance: false - no-cache: true - tags: ${{ steps.meta.outputs.tags }} diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml new file mode 100644 index 0000000..e68a117 --- /dev/null +++ b/.gitea/workflows/nightly.yml @@ -0,0 +1,55 @@ +name: Nightly Build + +on: + push: + branches: + - nightly + schedule: + - cron: '0 2 * * *' + workflow_dispatch: + +jobs: + build: + runs-on: self-hosted + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + clean: true + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Gitea registry + uses: docker/login-action@v3 + with: + registry: gitea.it-drui.de + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Build & push (amd64 + arm64) + run: | + DATE=$(date +%Y%m%d) + TAGS="gitea.it-drui.de/viewit/kx-bridge:nightly" + TAGS="$TAGS,gitea.it-drui.de/viewit/kx-bridge:nightly-$DATE" + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --push \ + --provenance=false \ + --no-cache \ + $(echo "$TAGS" | tr ',' '\n' | sed 's/^/-t /') \ + . + + - name: Set nightly tag + env: + GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }} + run: | + DATE=$(date +%Y%m%d) + TAG="nightly-$DATE" + git config user.name "gitea-actions" + git config user.email "actions@it-drui.de" + git tag -f "$TAG" + git push origin "$TAG" --force diff --git a/.gitea/workflows/pr-check.yml b/.gitea/workflows/pr-check.yml new file mode 100644 index 0000000..a815f3d --- /dev/null +++ b/.gitea/workflows/pr-check.yml @@ -0,0 +1,31 @@ +name: PR Check + +on: + pull_request: + branches: + - nightly + +jobs: + lint-and-test: + runs-on: self-hosted + steps: + - uses: actions/checkout@v4 + + - name: Python Setup + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Dependencies installieren + run: pip install -r requirements.txt + + - name: Lint + run: | + pip install flake8 + flake8 *.py --max-line-length=120 --extend-ignore=E501 + + - name: Tests + run: | + pip install pytest + pytest tests/ -v + if: ${{ hashFiles('tests/') != '' }} diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..d320088 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,58 @@ +name: Stable Release + +on: + push: + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +jobs: + release: + runs-on: self-hosted + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + clean: true + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Gitea registry + uses: docker/login-action@v3 + with: + registry: gitea.it-drui.de + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - 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 + }" diff --git a/.gitignore b/.gitignore index c8dfb6e..4d84a9f 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,8 @@ config/*.ini data/ !data/orca_filaments.json + +# Sensitive files — never commit .runner-token +secrets/ +*.token diff --git a/agents.md b/agents.md new file mode 100644 index 0000000..2ae11a6 --- /dev/null +++ b/agents.md @@ -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` From 394b0e69abe3ddef71905e6bfb0519fab560f407 Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 13:32:47 +0200 Subject: [PATCH 11/41] =?UTF-8?q?docs:=20CONTRIBUTING.md=20hinzuf=C3=BCgen?= =?UTF-8?q?=20(Fork-Flow,=20Branch-Modell,=20Commit-Stil)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CONTRIBUTING.md | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1b5eb74 --- /dev/null +++ b/CONTRIBUTING.md @@ -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. From 216b2de2c0a96c4ff84f95ff670113e5dbab4376 Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 13:35:48 +0200 Subject: [PATCH 12/41] fix: pull_request_template.md ins Root-Verzeichnis (Gitea-Anforderung) --- pull_request_template.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 pull_request_template.md diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 0000000..74818f8 --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,21 @@ +## Description + + +## 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 From 41f4700b24ab48f48af1636860e550fed4f1df6f Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 13:38:23 +0200 Subject: [PATCH 13/41] docs: Wartungshinweis + CONTRIBUTING.md Link in README (DE+EN) --- README.de.md | 9 +++++++++ README.md | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/README.de.md b/README.de.md index e6931e4..7677bae 100644 --- a/README.de.md +++ b/README.de.md @@ -15,6 +15,15 @@ Feedback willkommen. 🇬🇧 English version · 🇪🇸 Versión española +
+ +> [!CAUTION] +> **Laufende Wartungsarbeiten diese Woche** — Wir strukturieren das Repository um (Branch-Modell, CI-Workflows, Contribution-Prozess). Es kann zu Änderungen bei Branch-Namen, PR-Templates und der Art wie Releases veröffentlicht werden kommen. Wir entschuldigen uns für etwaige Unannehmlichkeiten. Handling, Workflow und die langfristige Wartbarkeit werden sich dadurch deutlich verbessern. +> +> 👉 Möchtest du beitragen? Bitte lies zuerst [CONTRIBUTING.md](CONTRIBUTING.md). + +
+
[![Ko-fi](https://img.shields.io/badge/Ko--fi-Support%20this%20project-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/viewitde) diff --git a/README.md b/README.md index 7bf3199..cdbceb2 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,15 @@ officially tested or supported. Feedback welcome. 🇩🇪 Deutsche Version · 🇪🇸 Versión española +
+ +> [!CAUTION] +> **Ongoing maintenance work this week** — 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. + +
+
[![Ko-fi](https://img.shields.io/badge/Ko--fi-Support%20this%20project-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/viewitde) From 3e1ba9df4b03d1e71693ba404a01eac1787d304f Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 13:39:31 +0200 Subject: [PATCH 14/41] docs: Wartungshinweis + CONTRIBUTING.md Link in README.es.md --- README.es.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.es.md b/README.es.md index 0a4931c..cebbf7b 100644 --- a/README.es.md +++ b/README.es.md @@ -14,6 +14,15 @@ ninguna está oficialmente probada ni soportada. Se agradece el feedback. 🇬🇧 English version · 🇩🇪 Deutsche Version +
+ +> [!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). + +
+
[![Ko-fi](https://img.shields.io/badge/Ko--fi-Apoya%20este%20proyecto-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/viewitde) From dc7e92688bdb150d29e4595e528c4caf0ab8427d Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 14:28:22 +0200 Subject: [PATCH 15/41] README.de.md aktualisiert --- README.de.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.de.md b/README.de.md index 7677bae..a6b08cf 100644 --- a/README.de.md +++ b/README.de.md @@ -112,13 +112,6 @@ Drucker → Verbindungstyp **Moonraker** → Host: `http://BRIDGE-IP:7125` > ⚠️ Verbindungstyp muss **Moonraker** sein (nicht „Bambu" oder „Klipper"). > Vollständige URL inkl. `http://` und Port `:7125` im Host-Feld eintragen. ---- - -## 📺 Video-Tutorial - -[![KX-Bridge Setup & Usage](https://img.youtube.com/vi/1Ql4wfH27fM/hqdefault.jpg)](https://www.youtube.com/watch?v=1Ql4wfH27fM) - ---- ## 🎨 Empfohlener Slicer From ffd8ed09d51e3affe11b31350c764e18e67a4b5c Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 14:28:52 +0200 Subject: [PATCH 16/41] README.es.md aktualisiert --- README.es.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.es.md b/README.es.md index cebbf7b..9cf78f9 100644 --- a/README.es.md +++ b/README.es.md @@ -139,11 +139,6 @@ Impresora → Tipo de conexión **Moonraker** → Host: `http://IP-DEL-PUENTE:71 --- -## 📺 Vídeo tutorial - -[![Configuración y uso de KX-Bridge](https://img.youtube.com/vi/1Ql4wfH27fM/hqdefault.jpg)](https://www.youtube.com/watch?v=1Ql4wfH27fM) - ---- ## 🎨 Slicer recomendado From 326453e2fda56ea67d0417f928ec0b42f1ccea2f Mon Sep 17 00:00:00 2001 From: viewit Date: Wed, 24 Jun 2026 14:29:15 +0200 Subject: [PATCH 17/41] README.md aktualisiert --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index cdbceb2..235d869 100644 --- a/README.md +++ b/README.md @@ -111,13 +111,7 @@ Printer → Connection type **Moonraker** → Host: `http://BRIDGE-IP:7125` > ⚠️ Connection type must be **Moonraker** (not "Bambu" or "Klipper"). > Enter the full URL including `http://` and port `:7125` in the host field. ---- -## 📺 Video Tutorial - -[![KX-Bridge Setup & Usage](https://img.youtube.com/vi/1Ql4wfH27fM/hqdefault.jpg)](https://www.youtube.com/watch?v=1Ql4wfH27fM) - ---- ## 🎨 Recommended Slicer From 812936d90d911914218a57086eaf21e191822cd4 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 10:52:57 +0200 Subject: [PATCH 18/41] =?UTF-8?q?fix(ci):=20setup-python=20entfernen=20?= =?UTF-8?q?=E2=80=94=20self-hosted=20Runner=20nutzt=20System-Python?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/pr-check.yml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/pr-check.yml b/.gitea/workflows/pr-check.yml index a815f3d..58f9df9 100644 --- a/.gitea/workflows/pr-check.yml +++ b/.gitea/workflows/pr-check.yml @@ -11,21 +11,16 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Python Setup - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - name: Dependencies installieren - run: pip install -r requirements.txt + run: pip3 install -r requirements.txt - name: Lint run: | - pip install flake8 + pip3 install flake8 flake8 *.py --max-line-length=120 --extend-ignore=E501 - name: Tests run: | - pip install pytest + pip3 install pytest pytest tests/ -v if: ${{ hashFiles('tests/') != '' }} From 93f72f0be9df90d4342ee87f423ad27178b64e0a Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 10:54:17 +0200 Subject: [PATCH 19/41] =?UTF-8?q?ci:=20nightly=20Build=20nur=20bei=20Code-?= =?UTF-8?q?=C3=84nderungen=20oder=20Cron=20(nicht=20bei=20Docs/Templates)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/nightly.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index e68a117..41441de 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -4,6 +4,12 @@ on: push: branches: - nightly + paths: + - '**.py' + - 'Dockerfile' + - 'requirements.txt' + - 'web/**' + - 'data/**' schedule: - cron: '0 2 * * *' workflow_dispatch: From 08d9a7ab0fbc25220380b4aaba2a0811359bbf2a Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 11:53:19 +0200 Subject: [PATCH 20/41] fix: [spoolman]-Section in config.ini.example nachgetragen (Issue #72) --- config/config.ini.example | 82 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/config/config.ini.example b/config/config.ini.example index b954b90..bea9879 100644 --- a/config/config.ini.example +++ b/config/config.ini.example @@ -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 From 73ecb1e618e1eef1ca4842617bfb94e29e113472 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 12:03:08 +0200 Subject: [PATCH 21/41] nightly: 0.9.27-nightly2 --- README.de.md | 16 +- README.md | 15 +- VERSION | 2 +- web/translations/de.json | 553 +++++++++++++++++++----------------- web/translations/en.json | 549 ++++++++++++++++++----------------- web/translations/es.json | 553 +++++++++++++++++++----------------- web/translations/fr.json | 553 +++++++++++++++++++----------------- web/translations/it.json | 549 ++++++++++++++++++----------------- web/translations/zh-cn.json | 553 +++++++++++++++++++----------------- 9 files changed, 1762 insertions(+), 1581 deletions(-) diff --git a/README.de.md b/README.de.md index a6b08cf..e6931e4 100644 --- a/README.de.md +++ b/README.de.md @@ -15,15 +15,6 @@ Feedback willkommen. 🇬🇧 English version · 🇪🇸 Versión española -
- -> [!CAUTION] -> **Laufende Wartungsarbeiten diese Woche** — Wir strukturieren das Repository um (Branch-Modell, CI-Workflows, Contribution-Prozess). Es kann zu Änderungen bei Branch-Namen, PR-Templates und der Art wie Releases veröffentlicht werden kommen. Wir entschuldigen uns für etwaige Unannehmlichkeiten. Handling, Workflow und die langfristige Wartbarkeit werden sich dadurch deutlich verbessern. -> -> 👉 Möchtest du beitragen? Bitte lies zuerst [CONTRIBUTING.md](CONTRIBUTING.md). - -
-
[![Ko-fi](https://img.shields.io/badge/Ko--fi-Support%20this%20project-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/viewitde) @@ -112,6 +103,13 @@ Drucker → Verbindungstyp **Moonraker** → Host: `http://BRIDGE-IP:7125` > ⚠️ Verbindungstyp muss **Moonraker** sein (nicht „Bambu" oder „Klipper"). > Vollständige URL inkl. `http://` und Port `:7125` im Host-Feld eintragen. +--- + +## 📺 Video-Tutorial + +[![KX-Bridge Setup & Usage](https://img.youtube.com/vi/1Ql4wfH27fM/hqdefault.jpg)](https://www.youtube.com/watch?v=1Ql4wfH27fM) + +--- ## 🎨 Empfohlener Slicer diff --git a/README.md b/README.md index 235d869..7bf3199 100644 --- a/README.md +++ b/README.md @@ -14,15 +14,6 @@ officially tested or supported. Feedback welcome. 🇩🇪 Deutsche Version · 🇪🇸 Versión española -
- -> [!CAUTION] -> **Ongoing maintenance work this week** — 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. - -
-
[![Ko-fi](https://img.shields.io/badge/Ko--fi-Support%20this%20project-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/viewitde) @@ -111,7 +102,13 @@ Printer → Connection type **Moonraker** → Host: `http://BRIDGE-IP:7125` > ⚠️ Connection type must be **Moonraker** (not "Bambu" or "Klipper"). > Enter the full URL including `http://` and port `:7125` in the host field. +--- +## 📺 Video Tutorial + +[![KX-Bridge Setup & Usage](https://img.youtube.com/vi/1Ql4wfH27fM/hqdefault.jpg)](https://www.youtube.com/watch?v=1Ql4wfH27fM) + +--- ## 🎨 Recommended Slicer diff --git a/VERSION b/VERSION index 26c2639..37c6438 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.27-nightly1 +0.9.27-nightly2 diff --git a/web/translations/de.json b/web/translations/de.json index 35ecc76..a8a8f66 100644 --- a/web/translations/de.json +++ b/web/translations/de.json @@ -1,296 +1,327 @@ { - "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", - "lbl_zpos": "Z (mm)", - "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 (Sekunden)", - "settings_version": "Version", + "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", - "settings_cat_display": "Darstellung", - "settings_cat_filament": "Filament", - "settings_cat_language": "Sprache", - "settings_cat_theme": "Hell / Dunkel umschalten", - "settings_filament_mapping": "Filament-Profil-Mapping (pro Slot)", - "settings_filament_mapping_save": "Mapping speichern", - "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_save": "Auswahl speichern", - "progress_action_print": "Drucken", - "progress_action_slots": "Slots zuordnen", - "progress_action_clear": "Leeren", - "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", + "nav_temps": "Temperaturen", + "orca_profile_done": "Importiert", + "orca_profile_dropmsg": "Hierher ziehen oder klicken", + "orca_profile_help_html": "Lade ein ZIP deines OrcaSlicer-Filament-Ordners oder einzelne .json-Files hoch.
In OrcaSlicer: Help → Show Configuration Folder → user/<id>/filament/", "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 ZIP deines OrcaSlicer-Filament-Ordners oder einzelne .json-Files hoch.
In OrcaSlicer: Help → Show Configuration Folder → user/<id>/filament/", - "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_objects_toggle": "Objekte überspringen", - "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}", - "store_upload_only_gcode": "✗ Nur GCode-Dateien erlaubt (.gcode, .3mf, .bgcode)", - "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_name": "A–Z Name", "ss_dur": "⏱ Druckzeit", - "ace_dry_preset_pla": "PLA", - "ace_dry_preset_pla_plus": "PLA+", - "ace_dry_preset_petg": "PETG", - "ace_dry_preset_tpu": "TPU", - "ace_dry_preset_abs_asa": "ABS / ASA", - "ace_dry_preset_pa_pc": "PA / PC", - "ace_dry_preset_custom": "Custom", - "fd_options_title": "Optionen", - "print_auto_leveling": "Auto-Leveling für diesen Druck", - "settings_file_ready_mode": "Druckdialog starten", - "settings_file_ready_banner": "Druckleiste", - "settings_file_ready_dialog": "Druckdialog", - "log_dir_rx": "RX", - "log_dir_tx": "TX", - "log_dir_label": "Richtung:", - "log_lvl_err": "⛔ Fehler", - "log_lvl_warn": "⚠ Warnung", - "log_topic_label": "Thema:", - "log_topic_ams": "AMS", - "log_topic_print": "Druck", - "log_topic_info": "Info", - "log_topic_status": "Status", - "log_download": "⬇ Download", - "log_auto": "⬇ Auto", - "log_clear": "✕ Leeren", - "log_filter_placeholder": "Filtern…", - "skip_cancel": "Abbrechen", - "skip_confirm": "Überspringen", - "settings_integrations": "Integrationen", - "modal_sec_spoolman": "Spoolman", - "lbl_spoolman_url": "Server-URL", - "lbl_spoolman_sync_rate": "Sync-Rate (s, 0=aus)", - "modal_sec_obico": "Obico" -} \ No newline at end of file + "ss_name": "A–Z Name", + "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_error": "Fehler", + "update_none": "Bereits aktuell", + "update_restarting": "Starte neu..." +} diff --git a/web/translations/en.json b/web/translations/en.json index 7359609..b08bf41 100644 --- a/web/translations/en.json +++ b/web/translations/en.json @@ -1,296 +1,327 @@ { - "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", - "lbl_zpos": "Z (mm)", - "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", - "ace_dry_preset_pla": "PLA", - "ace_dry_preset_pla_plus": "PLA+", - "ace_dry_preset_petg": "PETG", - "ace_dry_preset_tpu": "TPU", - "ace_dry_preset_abs_asa": "ABS / ASA", - "ace_dry_preset_pa_pc": "PA / PC", - "ace_dry_preset_custom": "Custom", - "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 (seconds)", + "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", - "settings_cat_display": "Appearance", - "settings_cat_filament": "Filament", - "settings_cat_language": "Language", - "settings_cat_theme": "Toggle light / dark", - "settings_filament_mapping": "Filament profile mapping (per slot)", - "settings_filament_mapping_save": "Save mapping", - "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_save": "Save selection", - "progress_action_print": "Print", - "progress_action_slots": "Map slots", - "progress_action_clear": "Clear", - "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 Default", - "fd_options_title": "Print Options", - "print_auto_leveling": "Auto-Leveling", - "settings_file_ready_mode": "Start Print Behavior", - "settings_file_ready_banner": "Print Bar", - "settings_file_ready_dialog": "Print Dialog", - "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", + "nav_temps": "Temperatures", + "orca_profile_done": "Imported", + "orca_profile_dropmsg": "Drop here or click", + "orca_profile_help_html": "Upload a ZIP of your OrcaSlicer filament folder or single .json files.
In OrcaSlicer: Help → Show Configuration Folder → user/<id>/filament/", "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 ZIP of your OrcaSlicer filament folder or single .json files.
In OrcaSlicer: Help → Show Configuration Folder → user/<id>/filament/", - "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_dir_rx": "RX", - "log_dir_tx": "TX", - "log_dir_label": "Dir:", - "log_lvl_label": "Level:", - "log_lvl_err": "⛔ Errors", - "log_lvl_warn": "⚠ Warn", - "log_topic_label": "Topic:", - "log_topic_ams": "AMS", - "log_topic_print": "Print", - "log_topic_info": "Info", - "log_topic_status": "Status", - "log_download": "⬇ Download", - "log_auto": "⬇ Auto", - "log_clear": "✕ Clear", - "log_filter_placeholder": "Filter…", - "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_objects_toggle": "Skip objects", - "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}", - "store_upload_only_gcode": "✗ Only GCode files allowed (.gcode, .3mf, .bgcode)", - "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_name": "A–Z Name", "ss_dur": "⏱ Print time", - "settings_integrations": "Integrations", - "modal_sec_spoolman": "Spoolman", - "lbl_spoolman_url": "Server URL", - "lbl_spoolman_sync_rate": "Sync rate (s, 0=off)", - "modal_sec_obico": "Obico" -} \ No newline at end of file + "ss_name": "A–Z Name", + "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_error": "Error", + "update_none": "Already up to date", + "update_restarting": "Restarting..." +} diff --git a/web/translations/es.json b/web/translations/es.json index dfca2a5..d418799 100644 --- a/web/translations/es.json +++ b/web/translations/es.json @@ -1,296 +1,327 @@ { - "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", - "lbl_zpos": "Z (mm)", - "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 (segundos)", + "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", - "settings_cat_display": "Apariencia", - "settings_cat_filament": "Filamento", - "settings_cat_language": "Idioma", - "settings_cat_theme": "Alternar claro / oscuro", - "settings_filament_mapping": "Asignación de perfil de filamento (por ranura)", - "settings_filament_mapping_save": "Guardar asignació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_save": "Guardar selección", - "progress_action_print": "Imprimir", - "progress_action_slots": "Asignar ranuras", - "progress_action_clear": "Vaciar", - "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", + "nav_temps": "Temperaturas", + "orca_profile_done": "Importado", + "orca_profile_dropmsg": "Suelta aquí o haz clic", + "orca_profile_help_html": "Sube un ZIP de tu carpeta de filamentos de OrcaSlicer o archivos .json sueltos.
En OrcaSlicer: Help → Show Configuration Folder → user/<id>/filament/", "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 ZIP de tu carpeta de filamentos de OrcaSlicer o archivos .json sueltos.
En OrcaSlicer: Help → Show Configuration Folder → user/<id>/filament/", - "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_objects_toggle": "Omitir objetos", - "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}", - "store_upload_only_gcode": "✗ Solo se permiten archivos GCode (.gcode, .3mf, .bgcode)", - "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_name": "A–Z Nombre", "ss_dur": "⏱ Tiempo de impresión", - "ace_dry_preset_pla": "PLA", - "ace_dry_preset_pla_plus": "PLA+", - "ace_dry_preset_petg": "PETG", - "ace_dry_preset_tpu": "TPU", - "ace_dry_preset_abs_asa": "ABS / ASA", - "ace_dry_preset_pa_pc": "PA / PC", - "ace_dry_preset_custom": "Personalizado", - "fd_options_title": "Opciones", - "print_auto_leveling": "Autonivelado para esta impresión", - "settings_file_ready_mode": "Iniciar diálogo de impresión", - "settings_file_ready_banner": "Barra de impresión", - "settings_file_ready_dialog": "Diálogo de impresión", - "log_dir_rx": "RX", - "log_dir_tx": "TX", - "log_dir_label": "Dirección:", - "log_lvl_err": "⛔ Errores", - "log_lvl_warn": "⚠ Avisos", - "log_topic_label": "Tema:", - "log_topic_ams": "AMS", - "log_topic_print": "Impresión", - "log_topic_info": "Info", - "log_topic_status": "Estado", - "log_download": "⬇ Descargar", - "log_auto": "⬇ Auto", - "log_clear": "✕ Limpiar", - "log_filter_placeholder": "Filtrar…", - "skip_cancel": "Cancelar", - "skip_confirm": "Omitir", - "settings_integrations": "Integraciones", - "modal_sec_spoolman": "Spoolman", - "lbl_spoolman_url": "URL del servidor", - "lbl_spoolman_sync_rate": "Tasa de sincronización (s, 0=desact.)", - "modal_sec_obico": "Obico" -} \ No newline at end of file + "ss_name": "A–Z Nombre", + "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_error": "Error", + "update_none": "Ya actualizado", + "update_restarting": "Reiniciando..." +} diff --git a/web/translations/fr.json b/web/translations/fr.json index bf4f0ba..863ba44 100644 --- a/web/translations/fr.json +++ b/web/translations/fr.json @@ -1,296 +1,327 @@ { - "header_status_standby": "Prêt", - "header_status_printing": "Impression", - "header_status_complete": "Terminé", - "header_status_error": "Erreur", - "kobra_free": "Disponible", - "kobra_busy": "Occupé", - "kobra_printing": "Impression", - "kobra_preheating": "Préchauffage", - "kobra_auto_leveling": "Mise à niveau auto", - "kobra_checking": "Vérification", - "kobra_updated": "Mise à jour", - "kobra_init": "Initialisation", - "kobra_pausing": "Pause en cours…", - "kobra_paused": "En pause", - "kobra_resuming": "Reprise en cours…", - "kobra_resumed": "Repris", - "kobra_stopping": "Arrêt en cours…", - "kobra_stoped": "Arrêté", - "kobra_finished": "Terminé", - "kobra_failed": "Erreur", - "kobra_canceled": "Annulé", - "kobra_offline": "Hors ligne", - "nav_dashboard": "Tableau de bord", - "nav_print": "Impression", - "nav_temps": "Températures", - "nav_motion": "Mouvement", - "nav_ams": "AMS", - "nav_extras": "Lumière / Ventilateur", - "nav_console": "Console", - "card_progress": "Progression", - "card_temps": "Températures", - "card_light_fan": "Ventilateur", - "card_speed": "Vitesse d'impression", - "card_cam": "Caméra", - "lbl_elapsed": "Écoulé :", - "lbl_remaining": "Restant :", - "lbl_slicer_time": "Estimation slicer :", - "lbl_layers": "Couche", - "lbl_zpos": "Z (mm)", - "speed_silent": "🐢 Silencieux", - "speed_normal": "⚡ Normal", - "speed_sport": "🚀 Sport", - "lbl_light": "💡 Lumière", - "lbl_feed": "Charger", - "lbl_unload": "Décharger", - "card_ace_dry": "Séchage ACE", + "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_humidity": "Humidité", - "ace_dry_current_temp": "Température", - "ace_dry_chart": "Historique (Temp/Humidité)", - "ace_dry_temp": "Température (°C)", - "ace_dry_duration": "Durée (min)", - "ace_dry_start": "▶ Démarrer", "ace_dry_stop": "■ Arrêter", - "ace_dry_auto_refill": "Remplissage auto", - "ace_dry_enable": "Activer le séchage", + "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)", - "ace_dry_dialog_title": "Réglages Temp/Durée du séchoir", - "ace_dry_dialog_temp": "Température (30-80°C)", - "ace_dry_dialog_time": "Temps restant (h:m:s)", - "ace_dry_dialog_confirm": "Confirmer", - "ace_dry_dialog_cancel": "Annuler", - "ace_dry_dialog_save_restart": "Enregistrer et redémarrer", - "ace_dry_dialog_custom_name": "Nom personnalisé", - "ace_dry_dialog_reset_default": "Réinitialiser", - "cam_placeholder": "📷 Caméra non démarrée", - "cam_stream_unavailable": "Flux indisponible", + "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", - "btn_cancel": "✕ Arrêter", - "label_nozzle": "Buse", + "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_on_off": "On / Off", - "label_speed": "Vitesse", - "panel_print_title": "Contrôle impression", - "panel_print_btn_pause": "⏸ Pause", - "panel_print_btn_resume": "▶ Reprendre", - "panel_print_btn_cancel": "✕ Annuler", - "panel_print_temps_live": "Températures (en direct)", - "label_set": "Définir", + "label_nozzle": "Buse", "label_off": "Éteint", - "panel_temps_nozzle": "Buse", - "panel_temps_bed": "Plateau chauffant", - "panel_temps_chart": "Historique (60 dernières valeurs)", - "label_target_c": "Cible :", - "panel_motion_xy": "Axes XY", - "panel_motion_z": "Axe Z", - "label_step": "Pas :", - "btn_home_z": "Origine Z", - "btn_home_xy": "Origine XY", - "btn_home_all": "Origine Tout", - "btn_disable_motors": "Moteurs Off", - "panel_ams_title": "Filament", - "card_ams": "Filament", - "ams_no_data": "Aucune donnée AMS reçue", + "label_on_off": "On / Off", + "label_set": "Définir", "label_slot": "Slot", - "ams_empty": "Vide", - "panel_extras_light": "Lumière", - "panel_extras_fan": "Ventilateur", - "panel_extras_camera": "Caméra", - "btn_cam_start2": "▶ Démarrer", - "btn_cam_stop2": "◼ Arrêter", - "panel_console_title": "Journal d'événements", - "log_light_on": "Lumière allumée", - "log_light_off": "Lumière éteinte", - "log_fan": "Ventilateur →", - "log_nozzle": "Buse →", - "log_bed": "Plateau →", + "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_home": "Origine", - "log_home_all": "Origine Tout", + "log_bed": "Plateau →", "log_cam_start": "Caméra démarrée :", "log_cam_stop": "Caméra arrêtée", - "log_poll_error": "Erreur de sondage :", + "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 :", - "confirm_cancel": "Vraiment annuler l'impression ?", - "settings_title": "Paramètres", - "settings_connection": "Connexion", - "settings_print": "Paramètres d'impression", - "settings_poll": "Intervalle de sondage (secondes)", + "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", - "settings_cat_display": "Apparence", - "settings_cat_filament": "Filament", - "settings_cat_language": "Langue", - "settings_cat_theme": "Basculer clair / sombre", - "settings_filament_mapping": "Mappage du profil de filament (par emplacement)", - "settings_filament_mapping_save": "Enregistrer le mappage", - "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_save": "Enregistrer la sélection", - "progress_action_print": "Imprimer", - "progress_action_slots": "Affecter les emplacements", - "progress_action_clear": "Vider", - "settings_version": "Version", - "settings_save": "Enregistrer et redémarrer", - "settings_printer_name": "Nom de l'imprimante", - "settings_printer_ip": "IP de l'imprimante", - "settings_mqtt_port": "Port MQTT", - "settings_username": "Nom d'utilisateur MQTT", - "settings_password": "Mot de passe MQTT", - "settings_device_id": "ID de l'appareil", - "settings_mode_id": "ID du mode", - "hint_ip_no_port": "Adresse IP uniquement, sans port (ex. 192.168.1.102)", - "settings_default_slot": "Slot par défaut (couleur unique)", - "settings_slot_auto": "Auto (tous les slots chargés)", - "settings_auto_leveling": "Mise à niveau auto avant impression", - "settings_camera_on_print": "Activer la caméra au démarrage de l'impression", - "settings_web_upload_warning": "Afficher un avertissement lors de l'impression de fichiers web", - "update_check": "Vérifier les mises à jour", - "update_checking": "Vérification…", - "update_available": "disponible", - "update_none": "Déjà à jour", - "update_apply": "Installer maintenant", - "update_applying": "Téléchargement…", - "update_restarting": "Redémarrage…", - "update_error": "Erreur", - "btn_connect": "⚡ Connecter", - "btn_disconnect": "✕ Déconnecter", - "lbl_conn_error": "Erreur de connexion :", - "slot_edit_title": "Modifier le slot", - "slot_edit_color": "Couleur", - "slot_edit_material": "Matériau", - "slot_edit_load": "⬇ Charger", - "slot_edit_unload": "⬆ Décharger", - "slot_edit_save": "💾 Enregistrer", - "slot_edit_custom": "ex. PLA, PETG, ABS…", - "slot_edit_ok": "Slot AMS", - "slot_edit_profile": "Profil OrcaSlicer", - "slot_edit_profile_hint": "Envoyé lors de la synchronisation OrcaSlicer comme marque spécifique au lieu de \"Générique\"", - "slot_edit_profile_default": "— Générique (défaut) —", - "orca_profile_section": "Profils OrcaSlicer", + "nav_temps": "Températures", + "orca_profile_done": "Importé", + "orca_profile_dropmsg": "Déposez ici ou cliquez", + "orca_profile_help_html": "Déposez un ZIP de votre dossier filament OrcaSlicer ou des fichiers .json individuels.
Dans OrcaSlicer : Aide → Afficher le dossier de configuration → user/<id>/filament/", "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_help_html": "Déposez un ZIP de votre dossier filament OrcaSlicer ou des fichiers .json individuels.
Dans OrcaSlicer : Aide → Afficher le dossier de configuration → user/<id>/filament/", - "orca_profile_dropmsg": "Déposez ici ou cliquez", "orca_profile_list_label": "Profils importés", - "orca_profile_user_label": "Mes profils", - "orca_profile_user_empty": "– aucun –", - "orca_profile_uploading": "Envoi en cours…", - "orca_profile_done": "Importé", + "orca_profile_section": "Profils OrcaSlicer", "orca_profile_skipped": "ignoré", - "log_dir_all": "Tout", - "log_lvl_label": "Niveau :", - "file_ready_btn": "▶ Lancer l'impression", - "file_slots_btn": "🎨 Choisir les slots", - "file_cancel_btn": "✕ Annuler", - "nav_printers": "Imprimantes", - "skip_title": "✂ Ignorer des objets", - "skip_hint": "Décochez les objets que vous ne souhaitez plus imprimer :", - "skip_btn_label": "Objets", - "skip_no_objects": "Aucun objet dans cette impression.", + "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.", - "fd_objects_hint": "Ignorer des objets (optionnel) :", - "fd_objects_toggle": "Ignorer des objets", - "fd_slots_hint": "Associer le canal GCode au slot AMS :", - "fd_cancel": "Annuler", - "fd_print": "▶ Imprimer", - "fd_no_slots_msg": "Aucun slot AMS chargé.{br}Lancer l'impression quand même ?", - "fd_slot": "Slot", - "fd_no_matching_material": "Aucun matériau correspondant", - "fd_used": "UTILISÉ", - "add_printer": "Ajouter une imprimante", - "apd_lbl_ip": "IP de l'imprimante", - "apd_lbl_name": "Nom (optionnel)", - "apd_placeholder_name": "ex. Kobra X Salon", - "apd_cancel": "Annuler", - "apd_confirm": "Ajouter", - "apd_fetching": "Récupération des données de l'imprimante…", - "apd_success": "Imprimante ajoutée, redémarrage du bridge…", - "apd_err_ip": "Veuillez saisir une adresse IP", - "printers_remove": "Supprimer l'imprimante", - "printers_remove_confirm": "Supprimer l'imprimante \"{name}\" ? Le bridge va redémarrer.", - "printers_active": "● actif", - "printers_switch": "Changer →", - "printers_current": "Imprimante actuelle", - "printers_loading": "Chargement…", - "printers_none": "Aucune imprimante configurée.", - "printers_empty_hint": "Aucune imprimante configurée.", - "nav_browser": "Navigateur", - "panel_browser_title": "Explorateur de fichiers", - "store_search_placeholder": "🔍 Rechercher…", - "store_empty": "Aucun fichier uploadé.", - "store_refresh": "↻ Actualiser", - "store_print": "▶ Imprimer", - "store_download": "⬇ Télécharger", - "store_delete_confirm": "Supprimer le fichier ?", - "store_print_confirm": "Imprimer le fichier ?", - "store_web_verify_title": "Vérifier le fichier", - "store_web_verify_msg": "Veuillez vérifier que ce fichier a été créé pour l'Anycubic Kobra X.", - "store_web_verify_confirm": "Confirmer", - "store_web_verify_abort": "Annuler", - "store_no_results": "Aucun fichier trouvé.", - "store_never": "jamais imprimé", - "store_estimate": "Estimation", - "store_upload_label_prefix": "Déposez un GCode ici ou ", - "store_upload_label_browse": "parcourir", - "store_upload_busy": "⏳ Envoi en cours…", - "store_upload_success": "✓ {file}", - "store_upload_error": "✗ {error}", - "store_upload_only_gcode": "✗ Seuls les fichiers GCode sont autorisés (.gcode, .3mf, .bgcode)", - "sf_all": "Tout", - "sf_ok": "✓ Terminés", - "sf_err": "✗ Échoués", - "sf_new": "Nouveau", + "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_name": "A–Z Nom", "ss_dur": "⏱ Durée d'impression", - "ace_dry_preset_pla": "PLA", - "ace_dry_preset_pla_plus": "PLA+", - "ace_dry_preset_petg": "PETG", - "ace_dry_preset_tpu": "TPU", - "ace_dry_preset_abs_asa": "ABS / ASA", - "ace_dry_preset_pa_pc": "PA / PC", - "ace_dry_preset_custom": "Personnalisé", - "fd_options_title": "Options", - "print_auto_leveling": "Mise à niveau auto pour cette impression", - "settings_file_ready_mode": "Démarrer le dialogue d'impression", - "settings_file_ready_banner": "Barre d'impression", - "settings_file_ready_dialog": "Dialogue d'impression", - "log_dir_rx": "RX", - "log_dir_tx": "TX", - "log_dir_label": "Sens :", - "log_lvl_err": "⛔ Erreurs", - "log_lvl_warn": "⚠ Avert.", - "log_topic_label": "Sujet :", - "log_topic_ams": "AMS", - "log_topic_print": "Impression", - "log_topic_info": "Info", - "log_topic_status": "Statut", - "log_download": "⬇ Télécharger", - "log_auto": "⬇ Auto", - "log_clear": "✕ Effacer", - "log_filter_placeholder": "Filtrer…", - "skip_cancel": "Annuler", - "skip_confirm": "Ignorer", - "settings_integrations": "Intégrations", - "modal_sec_spoolman": "Spoolman", - "lbl_spoolman_url": "URL du serveur", - "lbl_spoolman_sync_rate": "Taux de sync. (s, 0=désact.)", - "modal_sec_obico": "Obico" -} \ No newline at end of file + "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_error": "Erreur", + "update_none": "Déjà à jour", + "update_restarting": "Redémarrage…" +} diff --git a/web/translations/it.json b/web/translations/it.json index 4a9b504..4c50faa 100644 --- a/web/translations/it.json +++ b/web/translations/it.json @@ -1,296 +1,327 @@ { - "header_status_standby": "Pronto", - "header_status_printing": "In stampa", - "header_status_complete": "Completato", - "header_status_error": "Errore", - "kobra_free": "Pronto", - "kobra_busy": "Occupato", - "kobra_printing": "In stampa", - "kobra_preheating": "Preriscaldamento", - "kobra_auto_leveling": "Livellamento automatico", - "kobra_checking": "Verifica", - "kobra_updated": "Aggiornamento", - "kobra_init": "Inizializzazione", - "kobra_pausing": "Pausa in corso...", - "kobra_paused": "In pausa", - "kobra_resuming": "Ripresa...", - "kobra_resumed": "Ripreso", - "kobra_stopping": "Arresto...", - "kobra_stoped": "Arrestato", - "kobra_finished": "Finito", - "kobra_failed": "Errore", - "kobra_canceled": "Annullato", - "kobra_offline": "Offline", - "nav_dashboard": "Dashboard", - "nav_print": "Stampa", - "nav_temps": "Temperature", - "nav_motion": "Movimento", - "nav_ams": "AMS", - "nav_extras": "Luce / Ventola", - "nav_console": "Console", - "card_progress": "Avanzamento", - "card_temps": "Temperature", - "card_light_fan": "Ventola", - "card_speed": "Velocità di stampa", - "card_cam": "Camera", - "lbl_elapsed": "Trascorso:", - "lbl_remaining": "Rimanente:", - "lbl_slicer_time": "Stima slicer:", - "lbl_layers": "Layer", - "lbl_zpos": "Z (mm)", - "speed_silent": "🐢 Silenzioso", - "speed_normal": "⚡ Normale", - "speed_sport": "🚀 Sport", - "lbl_light": "💡 Luce", - "lbl_feed": "Carica", - "lbl_unload": "Rimuovi", - "card_ace_dry": "Essiccazione ACE", + "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_humidity": "Umidità", - "ace_dry_current_temp": "Temperatura", - "ace_dry_chart": "Cronologia (Temp/Umidità)", - "ace_dry_temp": "Temperatura (°C)", - "ace_dry_duration": "Durata (min)", - "ace_dry_start": "▶ Avvia", "ace_dry_stop": "■ Ferma", - "ace_dry_auto_refill": "Ricarica automatica", - "ace_dry_enable": "Abilita essiccazione", + "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)", - "ace_dry_dialog_title": "Impostazioni Temp/Tempo essiccatore", - "ace_dry_dialog_temp": "Temperatura (30-80°C)", - "ace_dry_dialog_time": "Tempo rim. (h:m:s)", - "ace_dry_dialog_confirm": "Conferma", - "ace_dry_dialog_cancel": "Annulla", - "ace_dry_dialog_save_restart": "Salva e riavvia", - "ace_dry_dialog_custom_name": "Nome personalizzato", - "ace_dry_dialog_reset_default": "Ripristina predefiniti", - "ace_dry_preset_pla": "PLA", - "ace_dry_preset_pla_plus": "PLA+", - "ace_dry_preset_petg": "PETG", - "ace_dry_preset_tpu": "TPU", - "ace_dry_preset_abs_asa": "ABS / ASA", - "ace_dry_preset_pa_pc": "PA / PC", - "ace_dry_preset_custom": "Personalizzato", - "cam_placeholder": "📷 Camera non avviata", - "cam_stream_unavailable": "Flusso video non disponibile", + "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", - "btn_cancel": "✕ Stop", - "label_nozzle": "Ugello", + "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_on_off": "On / Off", - "label_speed": "Velocità", - "panel_print_title": "Controllo stampa", - "panel_print_btn_pause": "⏸ Pausa", - "panel_print_btn_resume": "▶ Riprendi", - "panel_print_btn_cancel": "✕ Annulla", - "panel_print_temps_live": "Temperature (In tempo reale)", - "label_set": "Imposta", + "label_nozzle": "Ugello", "label_off": "Off", - "panel_temps_nozzle": "Ugello", - "panel_temps_bed": "Piatto riscaldato", - "panel_temps_chart": "Cronologia (ultime 60 letture)", - "label_target_c": "Target:", - "panel_motion_xy": "Assi XY", - "panel_motion_z": "Asse Z", - "label_step": "Ampiezza passo:", - "btn_home_z": "Home Z", - "btn_home_xy": "Home XY", - "btn_home_all": "Home generale", - "btn_disable_motors": "Spegni motori", - "panel_ams_title": "Filamento", - "card_ams": "Filamento", - "ams_no_data": "Nessun dato ricevuto dall' AMS", + "label_on_off": "On / Off", + "label_set": "Imposta", "label_slot": "Slot", - "ams_empty": "Vuoto", - "panel_extras_light": "Luce", - "panel_extras_fan": "Ventola", - "panel_extras_camera": "Camera", - "btn_cam_start2": "▶ Avvia", - "btn_cam_stop2": "◼ Ferma", - "panel_console_title": "Registro eventi", - "log_light_on": "Luce accesa", - "log_light_off": "Luce spenta", - "log_fan": "Ventola →", - "log_nozzle": "Ugello →", - "log_bed": "Piatto →", + "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_home": "Home", - "log_home_all": "Home generale", + "log_bed": "Piatto →", "log_cam_start": "Camera avviata:", "log_cam_stop": "Camera arrestata", - "log_poll_error": "Errore di sincronizzazione:", + "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:", - "confirm_cancel": "Annullare davvero la stampa?", - "settings_title": "Impostazioni", - "settings_connection": "Connessione", - "settings_print": "Impostazioni di stampa", - "settings_poll": "Intervallo di sincronizzazione (secondi)", + "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", - "settings_cat_display": "Aspetto", - "settings_cat_filament": "Filamento", - "settings_cat_language": "Lingua", - "settings_cat_theme": "Alterna chiaro / scuro", - "settings_filament_mapping": "Mappatura profilo filamento (per slot)", - "settings_filament_mapping_save": "Salva mappatura", - "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_save": "Salva selezione", - "progress_action_print": "Stampa", - "progress_action_slots": "Mappa slot", - "progress_action_clear": "Cancella", - "settings_version": "Versione", - "settings_save": "Salva e riavvia", - "settings_printer_name": "Nome stampante", - "settings_printer_ip": "IP stampante", - "settings_mqtt_port": "Porta MQTT", - "settings_username": "Nome utente MQTT", - "settings_password": "Password MQTT", - "settings_device_id": "ID dispositivo", - "settings_mode_id": "ID modalità", - "hint_ip_no_port": "Solo indirizzo IP, senza porta (es. 192.168.1.102)", - "settings_default_slot": "Slot predefinito (colore singolo)", - "settings_slot_auto": "Auto (tutti gli slot caricati)", - "settings_auto_leveling": "Livellamento automatico predefinito", - "fd_options_title": "Opzioni di stampa", - "print_auto_leveling": "Livellamento automatico", - "settings_file_ready_mode": "Comportamento all'avvio stampa", - "settings_file_ready_banner": "Barra di stampa", - "settings_file_ready_dialog": "Finestra di dialogo di stampa", - "settings_camera_on_print": "Attiva la camera all'avvio della stampa", - "settings_web_upload_warning": "Mostra un avviso quando si stampano caricamenti web", - "update_check": "Controlla aggiornamenti", - "update_checking": "Verifica in corso...", - "update_available": "disponibile", - "update_none": "Già aggiornato", - "update_apply": "Installa ora", - "update_applying": "Download in corso...", - "update_restarting": "Riavvio in corso...", - "update_error": "Errore", - "btn_connect": "⚡ Connetti", - "btn_disconnect": "✕ Disconnetti", - "lbl_conn_error": "Errore di connessione:", - "slot_edit_title": "Modifica slot", - "slot_edit_color": "Colore", - "slot_edit_material": "Materiale", - "slot_edit_load": "⬇ Carica", - "slot_edit_unload": "⬆ Rimuovi", - "slot_edit_save": "💾 Salva", - "slot_edit_custom": "es. PLA, PETG, ABS…", - "slot_edit_ok": "Slot AMS", - "slot_edit_profile": "Profilo OrcaSlicer", - "slot_edit_profile_hint": "Inviato durante la sincronizzazione con OrcaSlicer come marchio specifico invece di un semplice \"Generico\"", - "slot_edit_profile_default": "— Generico (predefinito) —", - "orca_profile_section": "Profili OrcaSlicer", + "nav_temps": "Temperature", + "orca_profile_done": "Importato", + "orca_profile_dropmsg": "Trascina qui o fai clic", + "orca_profile_help_html": "Carica un file ZIP della tua cartella filamenti di OrcaSlicer o file singoli .json.
In OrcaSlicer: Aiuto → Mostra cartella di configurazione → user/<id>/filament/", "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_help_html": "Carica un file ZIP della tua cartella filamenti di OrcaSlicer o file singoli .json.
In OrcaSlicer: Aiuto → Mostra cartella di configurazione → user/<id>/filament/", - "orca_profile_dropmsg": "Trascina qui o fai clic", "orca_profile_list_label": "Attualmente importati", - "orca_profile_user_label": "Profili personali", - "orca_profile_user_empty": "– nessuno –", - "orca_profile_uploading": "Caricamento in corso…", - "orca_profile_done": "Importato", + "orca_profile_section": "Profili OrcaSlicer", "orca_profile_skipped": "saltato", - "log_dir_all": "Tutti", - "log_dir_rx": "RX", - "log_dir_tx": "TX", - "log_dir_label": "Dir:", - "log_lvl_label": "Livello:", - "log_lvl_err": "⛔ Errori", - "log_lvl_warn": "⚠ Avvisi", - "log_topic_label": "Argomento:", - "log_topic_ams": "AMS", - "log_topic_print": "Stampa", - "log_topic_info": "Info", - "log_topic_status": "Stato", - "log_download": "⬇ Scarica", - "log_auto": "⬇ Auto", - "log_clear": "✕ Cancella", - "log_filter_placeholder": "Filtra…", - "file_ready_btn": "▶ Avvia stampa", - "file_slots_btn": "🎨 Seleziona slot", - "file_cancel_btn": "✕ Annulla", - "nav_printers": "Stampanti", - "skip_title": "✂ Salta oggetti", - "skip_hint": "Deseleziona gli oggetti che non vuoi più stampare:", - "skip_btn_label": "Oggetti", - "skip_no_objects": "Nessun oggetto in questa stampa.", + "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.", - "fd_objects_hint": "Salta oggetti (opzionale):", - "fd_objects_toggle": "Salta oggetti", - "fd_slots_hint": "Assegna il canale GCode allo slot AMS:", - "fd_cancel": "Annulla", - "fd_print": "▶ Stampa", - "fd_no_slots_msg": "Nessuno slot AMS caricato.{br}Avviare comunque la stampa?", - "fd_slot": "Slot", - "fd_no_matching_material": "Nessun materiale corrispondente", - "fd_used": "USATO", - "add_printer": "Aggiungi stampante", - "apd_lbl_ip": "IP stampante", - "apd_lbl_name": "Nome (opzionale)", - "apd_placeholder_name": "es. Kobra X Soggiorno", - "apd_cancel": "Annulla", - "apd_confirm": "Aggiungi", - "apd_fetching": "Recupero dati dalla stampante…", - "apd_success": "Stampante aggiunta, riavvio del bridge in corso…", - "apd_err_ip": "Inserisci un indirizzo IP", - "printers_remove": "Rimuovi stampante", - "printers_remove_confirm": "Rimuovere la stampante \"{name}\"? Il bridge si riavvierà.", - "printers_active": "● attiva", - "printers_switch": "Cambia →", - "printers_current": "Stampante corrente", - "printers_loading": "Caricamento in corso…", - "printers_none": "Nessuna stampante configurata.", - "printers_empty_hint": "Nessuna stampante ancora configurata.", - "nav_browser": "Browser", - "panel_browser_title": "Browser dei file", - "store_search_placeholder": "🔍 Cerca…", - "store_empty": "Nessun file caricato.", - "store_refresh": "↻ Aggiorna", - "store_print": "▶ Stampa", - "store_download": "⬇ Scarica", - "store_delete_confirm": "Eliminare il file?", - "store_print_confirm": "Stampare il file?", - "store_web_verify_title": "Verifica file", - "store_web_verify_msg": "Verifica che questo file sia stato creato per Anycubic Kobra X.", - "store_web_verify_confirm": "Conferma", - "store_web_verify_abort": "Interrompi", - "store_no_results": "Nessun file trovato.", - "store_never": "mai stampato", - "store_estimate": "Stima", - "store_upload_label_prefix": "Trascina il GCode qui o ", - "store_upload_label_browse": "sfoglia", - "store_upload_busy": "⏳ Caricamento in corso…", - "store_upload_success": "✓ {file}", - "store_upload_error": "✗ {error}", - "store_upload_only_gcode": "✗ Sono consentiti solo file GCode (.gcode, .3mf, .bgcode)", - "sf_all": "Tutti", - "sf_ok": "✓ Completato", - "sf_err": "✗ Fallito", - "sf_new": "Nuovo", + "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_name": "Nome A–Z", "ss_dur": "⏱ Tempo di stampa", - "settings_integrations": "Integrazioni", - "modal_sec_spoolman": "Spoolman", - "lbl_spoolman_url": "URL server", - "lbl_spoolman_sync_rate": "Frequenza sync (s, 0=disatt.)", - "modal_sec_obico": "Obico" -} \ No newline at end of file + "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_error": "Errore", + "update_none": "Già aggiornato", + "update_restarting": "Riavvio in corso..." +} diff --git a/web/translations/zh-cn.json b/web/translations/zh-cn.json index d376c21..7a29773 100644 --- a/web/translations/zh-cn.json +++ b/web/translations/zh-cn.json @@ -1,296 +1,327 @@ { - "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": "层", - "lbl_zpos": "Z (mm)", - "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": "轮询间隔(秒)", + "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": "设置", - "settings_cat_display": "外观", - "settings_cat_filament": "耗材", - "settings_cat_language": "语言", - "settings_cat_theme": "切换浅色 / 深色", - "settings_filament_mapping": "耗材配置映射(每槽位)", - "settings_filament_mapping_save": "保存映射", - "settings_visible_vendors": "可见厂商(配置下拉框)", - "settings_visible_vendors_hint": "仅这些厂商会出现在槽位配置下拉框中。未选择 = 显示全部。“Generic”和您自己的配置始终可见。", - "settings_visible_vendors_save": "保存选择", - "progress_action_print": "打印", - "progress_action_slots": "分配槽位", - "progress_action_clear": "清除", - "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 配置", + "nav_temps": "温度", + "orca_profile_done": "已导入", + "orca_profile_dropmsg": "拖到此处或点击", + "orca_profile_help_html": "上传 OrcaSlicer 耗材文件夹的 ZIP 或单个 .json 文件。
在 OrcaSlicer 中: Help → Show Configuration Folder → user/<id>/filament/", "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 耗材文件夹的 ZIP 或单个 .json 文件。
在 OrcaSlicer 中: Help → Show Configuration Folder → user/<id>/filament/", - "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_objects_toggle": "跳过对象", - "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}", - "store_upload_only_gcode": "✗ 仅允许 GCode 文件 (.gcode, .3mf, .bgcode)", - "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_name": "A–Z 名称", "ss_dur": "⏱ 打印时间", - "ace_dry_preset_pla": "PLA", - "ace_dry_preset_pla_plus": "PLA+", - "ace_dry_preset_petg": "PETG", - "ace_dry_preset_tpu": "TPU", - "ace_dry_preset_abs_asa": "ABS / ASA", - "ace_dry_preset_pa_pc": "PA / PC", - "ace_dry_preset_custom": "自定义", - "fd_options_title": "选项", - "print_auto_leveling": "本次打印自动调平", - "settings_file_ready_mode": "开始打印对话框", - "settings_file_ready_banner": "打印栏", - "settings_file_ready_dialog": "打印对话框", - "log_dir_rx": "RX", - "log_dir_tx": "TX", - "log_dir_label": "方向:", - "log_lvl_err": "⛔ 错误", - "log_lvl_warn": "⚠ 警告", - "log_topic_label": "主题:", - "log_topic_ams": "AMS", - "log_topic_print": "打印", - "log_topic_info": "信息", - "log_topic_status": "状态", - "log_download": "⬇ 下载", - "log_auto": "⬇ 自动", - "log_clear": "✕ 清空", - "log_filter_placeholder": "筛选…", - "skip_cancel": "取消", - "skip_confirm": "跳过", - "settings_integrations": "集成", - "modal_sec_spoolman": "Spoolman", - "lbl_spoolman_url": "服务器地址", - "lbl_spoolman_sync_rate": "同步频率(秒,0=关闭)", - "modal_sec_obico": "Obico" -} \ No newline at end of file + "ss_name": "A–Z 名称", + "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_error": "错误", + "update_none": "已是最新版本", + "update_restarting": "重启中..." +} From e753bcdb03e2929714f68df3abc8969aaee43e80 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 12:40:04 +0200 Subject: [PATCH 22/41] ci: Builds auf server-runner (Gitea-Server) umgestellt --- .gitea/workflows/nightly.yml | 2 +- .gitea/workflows/pr-check.yml | 2 +- .gitea/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index 41441de..4e6d1ce 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -16,7 +16,7 @@ on: jobs: build: - runs-on: self-hosted + runs-on: server-runner steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.gitea/workflows/pr-check.yml b/.gitea/workflows/pr-check.yml index 58f9df9..7f0e553 100644 --- a/.gitea/workflows/pr-check.yml +++ b/.gitea/workflows/pr-check.yml @@ -7,7 +7,7 @@ on: jobs: lint-and-test: - runs-on: self-hosted + runs-on: server-runner steps: - uses: actions/checkout@v4 diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index d320088..6f48f5c 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: release: - runs-on: self-hosted + runs-on: server-runner steps: - name: Checkout uses: actions/checkout@v4 From 29a4262a2a16c69ae8256416ac342eb69e649665 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 12:46:31 +0200 Subject: [PATCH 23/41] =?UTF-8?q?ci:=20Actions=20auf=20native=20Shell-Comm?= =?UTF-8?q?ands=20umgestellt=20(kein=20Node.js=20n=C3=B6tig)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/nightly.yml | 34 ++++++++++++++++++++-------------- .gitea/workflows/pr-check.yml | 10 +++++++++- .gitea/workflows/release.yml | 28 ++++++++++++++++++---------- 3 files changed, 47 insertions(+), 25 deletions(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index 4e6d1ce..c31720c 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -19,34 +19,40 @@ jobs: runs-on: server-runner steps: - name: Checkout - uses: actions/checkout@v4 - with: - clean: true + 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: Set up QEMU - uses: docker/setup-qemu-action@v3 + run: | + docker run --rm --privileged tonistiigi/binfmt:latest --install all - name: Set up buildx - uses: docker/setup-buildx-action@v3 + run: | + docker buildx inspect kxbuilder 2>/dev/null || \ + docker buildx create --name kxbuilder --use + docker buildx use kxbuilder - name: Login to Gitea registry - uses: docker/login-action@v3 - with: - registry: gitea.it-drui.de - username: ${{ secrets.REGISTRY_USER }} - password: ${{ secrets.REGISTRY_TOKEN }} + run: | + echo "${{ secrets.REGISTRY_TOKEN }}" | \ + docker login gitea.it-drui.de -u "${{ secrets.REGISTRY_USER }}" --password-stdin - name: Build & push (amd64 + arm64) run: | DATE=$(date +%Y%m%d) - TAGS="gitea.it-drui.de/viewit/kx-bridge:nightly" - TAGS="$TAGS,gitea.it-drui.de/viewit/kx-bridge:nightly-$DATE" docker buildx build \ --platform linux/amd64,linux/arm64 \ --push \ --provenance=false \ --no-cache \ - $(echo "$TAGS" | tr ',' '\n' | sed 's/^/-t /') \ + -t "gitea.it-drui.de/viewit/kx-bridge:nightly" \ + -t "gitea.it-drui.de/viewit/kx-bridge:nightly-$DATE" \ . - name: Set nightly tag @@ -58,4 +64,4 @@ jobs: git config user.name "gitea-actions" git config user.email "actions@it-drui.de" git tag -f "$TAG" - git push origin "$TAG" --force + git push https://gitea-actions:${GITEA_TOKEN}@gitea.it-drui.de/viewit/KX-Bridge-Release.git "$TAG" --force diff --git a/.gitea/workflows/pr-check.yml b/.gitea/workflows/pr-check.yml index 7f0e553..9a80a1a 100644 --- a/.gitea/workflows/pr-check.yml +++ b/.gitea/workflows/pr-check.yml @@ -9,7 +9,15 @@ jobs: lint-and-test: runs-on: server-runner steps: - - uses: actions/checkout@v4 + - 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 diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 6f48f5c..fe5a8e0 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -10,22 +10,30 @@ jobs: runs-on: server-runner steps: - name: Checkout - uses: actions/checkout@v4 - with: - clean: true + 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: Set up QEMU - uses: docker/setup-qemu-action@v3 + run: | + docker run --rm --privileged tonistiigi/binfmt:latest --install all - name: Set up buildx - uses: docker/setup-buildx-action@v3 + run: | + docker buildx inspect kxbuilder 2>/dev/null || \ + docker buildx create --name kxbuilder --use + docker buildx use kxbuilder - name: Login to Gitea registry - uses: docker/login-action@v3 - with: - registry: gitea.it-drui.de - username: ${{ secrets.REGISTRY_USER }} - password: ${{ secrets.REGISTRY_TOKEN }} + run: | + echo "${{ secrets.REGISTRY_TOKEN }}" | \ + docker login gitea.it-drui.de -u "${{ secrets.REGISTRY_USER }}" --password-stdin - name: Build & push (amd64 + arm64) run: | From fe1815c76fefa918a16e7d63aeda60456aef9019 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 12:51:00 +0200 Subject: [PATCH 24/41] ci: Docker CLI im Runner-Container per apt installieren --- .gitea/workflows/nightly.yml | 15 +++++++++++++++ .gitea/workflows/release.yml | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index c31720c..3b626f7 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -28,6 +28,21 @@ jobs: 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; then + apt-get update -qq + apt-get install -y -qq ca-certificates curl + install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc + chmod a+r /etc/apt/keyrings/docker.asc + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \ + > /etc/apt/sources.list.d/docker.list + apt-get update -qq + apt-get install -y -qq docker-ce-cli docker-buildx-plugin + fi + docker version --format '{{.Client.Version}}' + - name: Set up QEMU run: | docker run --rm --privileged tonistiigi/binfmt:latest --install all diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index fe5a8e0..a913bb6 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -20,6 +20,21 @@ jobs: 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; then + apt-get update -qq + apt-get install -y -qq ca-certificates curl + install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc + chmod a+r /etc/apt/keyrings/docker.asc + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \ + > /etc/apt/sources.list.d/docker.list + apt-get update -qq + apt-get install -y -qq docker-ce-cli docker-buildx-plugin + fi + docker version --format '{{.Client.Version}}' + - name: Set up QEMU run: | docker run --rm --privileged tonistiigi/binfmt:latest --install all From 77fce988d7b4baa2f785ab605b18dbc0edb0b0c4 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 12:54:02 +0200 Subject: [PATCH 25/41] ci: Docker CLI als statisches Binary installieren (Alpine-kompatibel) --- .gitea/workflows/nightly.yml | 23 ++++++++++++++--------- .gitea/workflows/release.yml | 23 ++++++++++++++--------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index 3b626f7..bf253f0 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -31,15 +31,20 @@ jobs: - name: Install Docker CLI run: | if ! command -v docker &>/dev/null; then - apt-get update -qq - apt-get install -y -qq ca-certificates curl - install -m 0755 -d /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc - chmod a+r /etc/apt/keyrings/docker.asc - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \ - > /etc/apt/sources.list.d/docker.list - apt-get update -qq - apt-get install -y -qq docker-ce-cli docker-buildx-plugin + ARCH=$(uname -m) + case "$ARCH" in + x86_64) ARCH=x86_64 ;; + aarch64) ARCH=aarch64 ;; + esac + DOCKER_VER=$(curl -fsSL https://download.docker.com/linux/static/stable/${ARCH}/ | grep -oP 'docker-\K[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -1) + curl -fsSL "https://download.docker.com/linux/static/stable/${ARCH}/docker-${DOCKER_VER}.tgz" | tar xz --strip-components=1 -C /usr/local/bin docker/docker + chmod +x /usr/local/bin/docker + # buildx plugin + BUILDX_VER=$(curl -fsSL https://api.github.com/repos/docker/buildx/releases/latest | grep -oP '"tag_name": "v\K[^"]+') + mkdir -p /usr/local/lib/docker/cli-plugins + curl -fsSL "https://github.com/docker/buildx/releases/download/v${BUILDX_VER}/buildx-v${BUILDX_VER}.linux-${ARCH/x86_64/amd64}" \ + -o /usr/local/lib/docker/cli-plugins/docker-buildx + chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx fi docker version --format '{{.Client.Version}}' diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index a913bb6..4888ffc 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -23,15 +23,20 @@ jobs: - name: Install Docker CLI run: | if ! command -v docker &>/dev/null; then - apt-get update -qq - apt-get install -y -qq ca-certificates curl - install -m 0755 -d /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc - chmod a+r /etc/apt/keyrings/docker.asc - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \ - > /etc/apt/sources.list.d/docker.list - apt-get update -qq - apt-get install -y -qq docker-ce-cli docker-buildx-plugin + ARCH=$(uname -m) + case "$ARCH" in + x86_64) ARCH=x86_64 ;; + aarch64) ARCH=aarch64 ;; + esac + DOCKER_VER=$(curl -fsSL https://download.docker.com/linux/static/stable/${ARCH}/ | grep -oP 'docker-\K[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -1) + curl -fsSL "https://download.docker.com/linux/static/stable/${ARCH}/docker-${DOCKER_VER}.tgz" | tar xz --strip-components=1 -C /usr/local/bin docker/docker + chmod +x /usr/local/bin/docker + # buildx plugin + BUILDX_VER=$(curl -fsSL https://api.github.com/repos/docker/buildx/releases/latest | grep -oP '"tag_name": "v\K[^"]+') + mkdir -p /usr/local/lib/docker/cli-plugins + curl -fsSL "https://github.com/docker/buildx/releases/download/v${BUILDX_VER}/buildx-v${BUILDX_VER}.linux-${ARCH/x86_64/amd64}" \ + -o /usr/local/lib/docker/cli-plugins/docker-buildx + chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx fi docker version --format '{{.Client.Version}}' From be110fd766e06b5c19d8b76052d1f1338f3e7610 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 12:56:38 +0200 Subject: [PATCH 26/41] ci: Docker CLI Installation BusyBox-kompatibel (wget, fixe Versionen) --- .gitea/workflows/nightly.yml | 23 ++++++++++++----------- .gitea/workflows/release.yml | 23 ++++++++++++----------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index bf253f0..965b7e8 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -30,20 +30,21 @@ jobs: - name: Install Docker CLI run: | - if ! command -v docker &>/dev/null; then + if ! command -v docker >/dev/null 2>&1; then ARCH=$(uname -m) - case "$ARCH" in - x86_64) ARCH=x86_64 ;; - aarch64) ARCH=aarch64 ;; - esac - DOCKER_VER=$(curl -fsSL https://download.docker.com/linux/static/stable/${ARCH}/ | grep -oP 'docker-\K[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -1) - curl -fsSL "https://download.docker.com/linux/static/stable/${ARCH}/docker-${DOCKER_VER}.tgz" | tar xz --strip-components=1 -C /usr/local/bin docker/docker + 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 - # buildx plugin - BUILDX_VER=$(curl -fsSL https://api.github.com/repos/docker/buildx/releases/latest | grep -oP '"tag_name": "v\K[^"]+') mkdir -p /usr/local/lib/docker/cli-plugins - curl -fsSL "https://github.com/docker/buildx/releases/download/v${BUILDX_VER}/buildx-v${BUILDX_VER}.linux-${ARCH/x86_64/amd64}" \ - -o /usr/local/lib/docker/cli-plugins/docker-buildx + 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}}' diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 4888ffc..0bed196 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -22,20 +22,21 @@ jobs: - name: Install Docker CLI run: | - if ! command -v docker &>/dev/null; then + if ! command -v docker >/dev/null 2>&1; then ARCH=$(uname -m) - case "$ARCH" in - x86_64) ARCH=x86_64 ;; - aarch64) ARCH=aarch64 ;; - esac - DOCKER_VER=$(curl -fsSL https://download.docker.com/linux/static/stable/${ARCH}/ | grep -oP 'docker-\K[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -1) - curl -fsSL "https://download.docker.com/linux/static/stable/${ARCH}/docker-${DOCKER_VER}.tgz" | tar xz --strip-components=1 -C /usr/local/bin docker/docker + 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 - # buildx plugin - BUILDX_VER=$(curl -fsSL https://api.github.com/repos/docker/buildx/releases/latest | grep -oP '"tag_name": "v\K[^"]+') mkdir -p /usr/local/lib/docker/cli-plugins - curl -fsSL "https://github.com/docker/buildx/releases/download/v${BUILDX_VER}/buildx-v${BUILDX_VER}.linux-${ARCH/x86_64/amd64}" \ - -o /usr/local/lib/docker/cli-plugins/docker-buildx + 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}}' From d7c2dccef5292cd692074453add9108207bd6541 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 22:50:08 +0200 Subject: [PATCH 27/41] ci: Gitea Nightly-Release mit Changelog nach erfolgreichem Build --- .gitea/workflows/nightly.yml | 54 +++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index 965b7e8..80afc14 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -76,13 +76,65 @@ jobs: -t "gitea.it-drui.de/viewit/kx-bridge:nightly-$DATE" \ . - - name: Set nightly tag + - name: Create Gitea Nightly Release env: GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | DATE=$(date +%Y%m%d) TAG="nightly-$DATE" + VERSION=$(cat 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="## KX-Bridge ${VERSION} — Nightly Build + +> ⚠️ **Experimentell** — ungetestete Features, nur für Tester geeignet. + +### Änderungen seit \`${PREV_TAG:-erstem Commit}\` + +${COMMITS} + +--- + +### Docker-Image aktualisieren + +\`\`\`bash +docker compose pull && docker compose up -d +\`\`\` + +Image-Tag: \`gitea.it-drui.de/viewit/kx-bridge:nightly\`" + + # Tag setzen git tag -f "$TAG" git push https://gitea-actions:${GITEA_TOKEN}@gitea.it-drui.de/viewit/KX-Bridge-Release.git "$TAG" --force + + # Altes nightly-Release löschen falls vorhanden (Datum-Tag von heute) + 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 + 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\": \"${TAG}\", + \"name\": \"KX-Bridge ${VERSION} Nightly (${DATE})\", + \"body\": $(printf '%s' "$BODY" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))'), + \"draft\": false, + \"prerelease\": true + }" From e7c978a0678e59b946d5b757e9f4cb4f08725878 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 22:53:58 +0200 Subject: [PATCH 28/41] ci: nightly.yml YAML-Syntaxfehler beheben (kein Multiline-Body im YAML) --- .gitea/workflows/nightly.yml | 37 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index 80afc14..c6342d1 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -98,43 +98,30 @@ jobs: fi [ -z "$COMMITS" ] && COMMITS="- Automatischer Nightly-Build" - BODY="## KX-Bridge ${VERSION} — Nightly Build - -> ⚠️ **Experimentell** — ungetestete Features, nur für Tester geeignet. - -### Änderungen seit \`${PREV_TAG:-erstem Commit}\` - -${COMMITS} - ---- - -### Docker-Image aktualisieren - -\`\`\`bash -docker compose pull && docker compose up -d -\`\`\` - -Image-Tag: \`gitea.it-drui.de/viewit/kx-bridge:nightly\`" + # 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 - # Altes nightly-Release löschen falls vorhanden (Datum-Tag von heute) + # Altes nightly-Release loeschen falls vorhanden (Datum-Tag von heute) 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 + BODY_JSON=$(python3 -c "import sys,json; print(json.dumps(open(sys.argv[1]).read()))" "$BODY_FILE") 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\": \"${TAG}\", - \"name\": \"KX-Bridge ${VERSION} Nightly (${DATE})\", - \"body\": $(printf '%s' "$BODY" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))'), - \"draft\": false, - \"prerelease\": true - }" + -d "{\"tag_name\":\"${TAG}\",\"name\":\"KX-Bridge ${VERSION} Nightly (${DATE})\",\"body\":${BODY_JSON},\"draft\":false,\"prerelease\":true}" + rm -f "$BODY_FILE" From 38d98666c494163057d367b962ea9e4c116ce2a2 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 23:01:28 +0200 Subject: [PATCH 29/41] ci: python3 durch awk ersetzen (nicht im Runner-Image) --- .gitea/workflows/nightly.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index c6342d1..866388a 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -117,8 +117,11 @@ jobs: "https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases/tags/${TAG}" \ 2>/dev/null || true - # Release erstellen - BODY_JSON=$(python3 -c "import sys,json; print(json.dumps(open(sys.argv[1]).read()))" "$BODY_FILE") + # Release erstellen (JSON-Body via awk escapen, kein python3 nötig) + BODY_JSON=$(awk '{ + gsub(/\\/, "\\\\"); gsub(/"/, "\\\""); gsub(/\t/, "\\t"); + printf "%s\\n", $0 + }' "$BODY_FILE" | awk 'BEGIN{printf "\""} {printf "%s", $0} END{printf "\""}') curl -s -X POST \ -H "Authorization: token ${GITEA_TOKEN}" \ -H "Content-Type: application/json" \ From ac309d5d3d9407e9a35f1d842705f7e3cf7a4165 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 23:01:34 +0200 Subject: [PATCH 30/41] nightly: 0.9.27-nightly4 --- VERSION | 2 +- kobrax_moonraker_bridge.py | 90 ++++++++++++++++++++----------------- web/themes/default/app.js | 38 +++++++++++++--- web/translations/de.json | 2 + web/translations/en.json | 2 + web/translations/es.json | 2 + web/translations/fr.json | 2 + web/translations/it.json | 2 + web/translations/zh-cn.json | 2 + 9 files changed, 93 insertions(+), 49 deletions(-) diff --git a/VERSION b/VERSION index 0dfdb9c..4aab532 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.27-nightly3 +0.9.27-nightly4 diff --git a/kobrax_moonraker_bridge.py b/kobrax_moonraker_bridge.py index 38981f4..b094b3b 100644 --- a/kobrax_moonraker_bridge.py +++ b/kobrax_moonraker_bridge.py @@ -812,40 +812,6 @@ class SpoolmanClient: json={"use_length": round(use_length_mm, 2)}) -class SpoolmanClient: - """Thin synchronous HTTP client for Spoolman filament tracking. - - Designed to be called from daemon threads (poll loop, _on_print callbacks). - Uses requests (already in requirements) so no event-loop dependency. - """ - - def __init__(self, server_url: str, sync_rate: int = 0): - self.server_url = server_url.rstrip("/") - self.sync_rate = sync_rate - - def _req(self, method: str, path: str, **kwargs): - import requests - r = requests.request(method, f"{self.server_url}{path}", timeout=5, **kwargs) - r.raise_for_status() - return r.json() - - def health_check(self) -> bool: - try: - self._req("GET", "/api/v1/health") - return True - except Exception: - return False - - def list_spools(self) -> list: - return self._req("GET", "/api/v1/spool") - - def use_filament(self, spool_id: int, use_length_mm: float) -> None: - """Report consumed filament length in mm. Spoolman converts to weight - using the spool's filament profile density.""" - self._req("PUT", f"/api/v1/spool/{spool_id}/use", - json={"use_length": round(use_length_mm, 2)}) - - class KobraXBridge: def __init__(self, client: KobraXClient, args=None, store=None, printer_id: str = "1", all_bridges=None): self.client = client @@ -4558,9 +4524,10 @@ class KobraXBridge: # ─── Update ────────────────────────────────────────────────────────────── - STABLE_RELEASE_API = "https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases?limit=1" - DEV_RELEASE_API = "https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases?limit=10&pre-release=true" - GITEA_RAW_BASE = "https://gitea.it-drui.de/viewit/KX-Bridge-Release/raw/tag" + STABLE_RELEASE_API = "https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases?limit=1" + NIGHTLY_RELEASE_API = "https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases?limit=5&pre-release=true" + DEV_RELEASE_API = "https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases?limit=10&pre-release=true" + GITEA_RAW_BASE = "https://gitea.it-drui.de/viewit/KX-Bridge-Release/raw/tag" def _read_version(self) -> str: # PyInstaller-Onefile entpackt VERSION (per kx-bridge.spec datas) nach @@ -4635,8 +4602,14 @@ class KobraXBridge: async def handle_api_update_check(self, request): current = self._read_version() + is_nightly = "nightly" in current is_dev = "-dev+" in current - api_url = self.DEV_RELEASE_API if is_dev else self.STABLE_RELEASE_API + if is_nightly: + api_url = self.NIGHTLY_RELEASE_API + elif is_dev: + api_url = self.DEV_RELEASE_API + else: + api_url = self.STABLE_RELEASE_API try: async with aiohttp.ClientSession() as session: async with session.get(api_url, timeout=aiohttp.ClientTimeout(total=10)) as resp: @@ -4645,14 +4618,44 @@ class KobraXBridge: releases = await resp.json(content_type=None) if not releases: return web.json_response({"error": "Keine Releases gefunden"}, status=404) - # Dev: neuestes Release mit "-dev+" im Tag suchen - if is_dev: + + if is_nightly: + # Neuestes Prerelease mit nightly-Tag suchen + nightly_releases = [r for r in releases if r.get("prerelease") and "nightly" in r.get("tag_name", "")] + if not nightly_releases: + return web.json_response({"error": "Keine Nightly-Releases gefunden"}, status=404) + data = nightly_releases[0] + tag = data.get("tag_name", "") + # Vergleich über Datum im Tag: nightly-YYYYMMDD vs aktuellem Datum in VERSION + # Aktuelles Datum aus VERSION extrahieren (Format: 0.9.27-nightlyN oder nightly-YYYYMMDD) + import re as _re + latest_date = _re.search(r"nightly-(\d{8})", tag) + current_date = _re.search(r"nightly-(\d{8})", current) + if latest_date and current_date: + update_available = latest_date.group(1) > current_date.group(1) + else: + # Fallback: Tag-Name unterschiedlich = Update verfügbar + update_available = tag != current and tag != f"nightly-{current}" + latest = tag + return web.json_response({ + "current": current, + "latest": latest, + "update_available": update_available, + "tag": tag, + "docker_only": True, + "changelog": data.get("body", ""), + }) + elif is_dev: dev_releases = [r for r in releases if "-dev+" in r.get("tag_name", "")] if not dev_releases: return web.json_response({"error": "Keine Dev-Releases gefunden"}, status=404) data = dev_releases[0] else: - data = releases[0] + # Stable: nur non-prerelease nehmen + stable_releases = [r for r in releases if not r.get("prerelease")] + if not stable_releases: + return web.json_response({"error": "Keine Stable-Releases gefunden"}, status=404) + data = stable_releases[0] tag = data.get("tag_name", "") latest = tag.lstrip("v") if is_dev: @@ -4666,6 +4669,7 @@ class KobraXBridge: "update_available": update_available, "tag": tag, "download_url": download_url, + "docker_only": False, "changelog": data.get("body", ""), }) except Exception as e: @@ -4686,6 +4690,10 @@ class KobraXBridge: async def handle_api_update_apply(self, request): data = await request.json() new_tag = data.get("tag", "") + if "nightly" in self._read_version(): + return web.json_response( + {"error": "Nightly-Updates laufen über Docker: " + "docker compose pull && docker compose up -d"}, status=400) if getattr(sys, "frozen", False): return web.json_response( {"error": "Self-Update wird im Binary-Modus nicht unterstützt – " diff --git a/web/themes/default/app.js b/web/themes/default/app.js index 3e8d6d4..082bb37 100644 --- a/web/themes/default/app.js +++ b/web/themes/default/app.js @@ -1661,21 +1661,33 @@ function saveSettings(){ clog('Settings-Fehler: '+e,'msg-err'); }); } +var _updateDockerOnly=false; function checkUpdate(){ var sb=document.getElementById('update-status'); sb.textContent=T.update_checking; document.getElementById('btn-update-apply').style.display='none'; - _updateTag='';_updateUrl=''; + _updateTag='';_updateUrl='';_updateDockerOnly=false; fetch(_apiUrl('/api/update/check')).then(function(r){return r.json()}).then(function(d){ if(d.error){sb.textContent=T.update_error+': '+d.error;return;} var cl=document.getElementById('update-changelog'); - if(d.changelog&&d.changelog.trim()){cl.textContent=d.changelog;cl.style.display='block';} - else{cl.style.display='none';} + if(d.changelog&&d.changelog.trim()){ + // Changelog als Markdown-Text (pre-formatiert) anzeigen + cl.textContent=d.changelog;cl.style.display='block'; + } else {cl.style.display='none';} + _updateDockerOnly=!!d.docker_only; if(d.update_available){ - sb.textContent='v'+d.latest+' '+T.update_available; + sb.textContent=d.latest+' '+T.update_available; sb.style.color='var(--ok)'; - _updateTag=d.tag;_updateUrl=d.download_url; - document.getElementById('btn-update-apply').style.display='inline-block'; + _updateTag=d.tag;_updateUrl=d.download_url||''; + var btn=document.getElementById('btn-update-apply'); + if(_updateDockerOnly){ + btn.textContent=T.update_docker||'docker compose pull'; + btn.title='docker compose pull && docker compose up -d'; + } else { + btn.textContent=T.update_apply; + btn.title=''; + } + btn.style.display='inline-block'; } else { sb.textContent=T.update_none; sb.style.color='var(--txt2)'; @@ -1683,9 +1695,21 @@ function checkUpdate(){ }).catch(function(e){sb.textContent=T.update_error+': '+e;}); } function applyUpdate(){ - if(!_updateUrl)return; var sb=document.getElementById('update-status'); var btn=document.getElementById('btn-update-apply'); + if(_updateDockerOnly){ + // Nightly: kein Self-Update, Docker-Befehl in Zwischenablage kopieren + var cmd='docker compose pull && docker compose up -d'; + if(navigator.clipboard){ + navigator.clipboard.writeText(cmd).then(function(){ + sb.textContent=T.update_docker_copied||'Befehl kopiert: '+cmd; + }); + } else { + sb.textContent=cmd; + } + return; + } + if(!_updateUrl)return; btn.disabled=true;sb.textContent=T.update_applying; post('/api/update/apply',{download_url:_updateUrl,tag:_updateTag}).then(function(){ sb.textContent=T.update_restarting; diff --git a/web/translations/de.json b/web/translations/de.json index a8a8f66..89815b4 100644 --- a/web/translations/de.json +++ b/web/translations/de.json @@ -321,6 +321,8 @@ "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..." diff --git a/web/translations/en.json b/web/translations/en.json index b08bf41..6b28894 100644 --- a/web/translations/en.json +++ b/web/translations/en.json @@ -321,6 +321,8 @@ "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..." diff --git a/web/translations/es.json b/web/translations/es.json index d418799..1782c68 100644 --- a/web/translations/es.json +++ b/web/translations/es.json @@ -321,6 +321,8 @@ "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..." diff --git a/web/translations/fr.json b/web/translations/fr.json index 863ba44..5f3cd3f 100644 --- a/web/translations/fr.json +++ b/web/translations/fr.json @@ -321,6 +321,8 @@ "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…" diff --git a/web/translations/it.json b/web/translations/it.json index 4c50faa..b06d4a1 100644 --- a/web/translations/it.json +++ b/web/translations/it.json @@ -321,6 +321,8 @@ "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..." diff --git a/web/translations/zh-cn.json b/web/translations/zh-cn.json index 7a29773..0df7ce4 100644 --- a/web/translations/zh-cn.json +++ b/web/translations/zh-cn.json @@ -321,6 +321,8 @@ "update_available": "可用", "update_check": "检查更新", "update_checking": "检查中...", + "update_docker": "复制命令", + "update_docker_copied": "已复制!执行:docker compose pull && docker compose up -d", "update_error": "错误", "update_none": "已是最新版本", "update_restarting": "重启中..." From bc9bfb58ea40b3d9ad5b00e949085a99cda4575d Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 23:29:19 +0200 Subject: [PATCH 31/41] ci: TAG aus VERSION statt Datum, curl durch wget ersetzen --- .gitea/workflows/nightly.yml | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index 866388a..a3376ef 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -66,23 +66,22 @@ jobs: - name: Build & push (amd64 + arm64) run: | - DATE=$(date +%Y%m%d) + 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-$DATE" \ + -t "gitea.it-drui.de/viewit/kx-bridge:nightly-${VERSION}" \ . - name: Create Gitea Nightly Release env: GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | - DATE=$(date +%Y%m%d) - TAG="nightly-$DATE" VERSION=$(cat VERSION) + TAG="nightly-${VERSION}" git config user.name "gitea-actions" git config user.email "actions@it-drui.de" @@ -111,20 +110,23 @@ jobs: git tag -f "$TAG" git push https://gitea-actions:${GITEA_TOKEN}@gitea.it-drui.de/viewit/KX-Bridge-Release.git "$TAG" --force - # Altes nightly-Release loeschen falls vorhanden (Datum-Tag von heute) - curl -s -X DELETE \ - -H "Authorization: token ${GITEA_TOKEN}" \ + # Altes Release loeschen falls vorhanden + wget -q --method=DELETE \ + --header="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, kein python3 nötig) + # Release erstellen (JSON-Body via awk escapen, kein python3/curl nötig) BODY_JSON=$(awk '{ gsub(/\\/, "\\\\"); gsub(/"/, "\\\""); gsub(/\t/, "\\t"); printf "%s\\n", $0 }' "$BODY_FILE" | awk 'BEGIN{printf "\""} {printf "%s", $0} END{printf "\""}') - 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\":\"${TAG}\",\"name\":\"KX-Bridge ${VERSION} Nightly (${DATE})\",\"body\":${BODY_JSON},\"draft\":false,\"prerelease\":true}" - rm -f "$BODY_FILE" + 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 + wget -q -O- \ + --method=POST \ + --header="Authorization: token ${GITEA_TOKEN}" \ + --header="Content-Type: application/json" \ + --body-file=/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 From 2f59a2b02b3270fea592ab4506d87add01e06b3e Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 23:29:34 +0200 Subject: [PATCH 32/41] nightly: 0.9.27-nightly5 --- VERSION | 2 +- kobrax_moonraker_bridge.py | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/VERSION b/VERSION index 4aab532..ea7550c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.27-nightly4 +0.9.27-nightly5 diff --git a/kobrax_moonraker_bridge.py b/kobrax_moonraker_bridge.py index b094b3b..42bc9e7 100644 --- a/kobrax_moonraker_bridge.py +++ b/kobrax_moonraker_bridge.py @@ -4626,16 +4626,9 @@ class KobraXBridge: return web.json_response({"error": "Keine Nightly-Releases gefunden"}, status=404) data = nightly_releases[0] tag = data.get("tag_name", "") - # Vergleich über Datum im Tag: nightly-YYYYMMDD vs aktuellem Datum in VERSION - # Aktuelles Datum aus VERSION extrahieren (Format: 0.9.27-nightlyN oder nightly-YYYYMMDD) - import re as _re - latest_date = _re.search(r"nightly-(\d{8})", tag) - current_date = _re.search(r"nightly-(\d{8})", current) - if latest_date and current_date: - update_available = latest_date.group(1) > current_date.group(1) - else: - # Fallback: Tag-Name unterschiedlich = Update verfügbar - update_available = tag != current and tag != f"nightly-{current}" + # Tag-Format: "nightly-0.9.27-nightly4", current: "0.9.27-nightly4" + tag_version = tag[len("nightly-"):] if tag.startswith("nightly-") else tag + update_available = tag_version != current latest = tag return web.json_response({ "current": current, From e5b2a19192491e0935bbdd35ce8c4b421e89fe5c Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 23:41:25 +0200 Subject: [PATCH 33/41] =?UTF-8?q?ci:=20curl=20via=20apk/static-binary=20st?= =?UTF-8?q?att=20BusyBox-wget=20f=C3=BCr=20API-Calls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/nightly.yml | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index a3376ef..cd528de 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -110,23 +110,30 @@ jobs: 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 + apk add --no-cache curl 2>/dev/null || \ + 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 + # Altes Release loeschen falls vorhanden - wget -q --method=DELETE \ - --header="Authorization: token ${GITEA_TOKEN}" \ + 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, kein python3/curl nötig) + # 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 - wget -q -O- \ - --method=POST \ - --header="Authorization: token ${GITEA_TOKEN}" \ - --header="Content-Type: application/json" \ - --body-file=/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 From eb7fd44f68e9c437aa9534bb5c57a55a5ba797e9 Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 23:41:36 +0200 Subject: [PATCH 34/41] nightly: 0.9.27-nightly6 --- README.de.md | 27 +++++++++++++++++++++++++-- README.md | 27 +++++++++++++++++++++++++-- VERSION | 2 +- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/README.de.md b/README.de.md index e6931e4..2cbdfb1 100644 --- a/README.de.md +++ b/README.de.md @@ -21,7 +21,7 @@ Feedback willkommen.   [![Releases](https://img.shields.io/badge/Download-Releases-2EA043?style=for-the-badge&logo=gitea&logoColor=white)](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)   -[![Downloads](https://img.shields.io/badge/Downloads-800%2B-8957E5?style=for-the-badge&logo=gitea&logoColor=white)](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases) +[![Downloads](https://img.shields.io/badge/Downloads-3.1k%2B-8957E5?style=for-the-badge&logo=gitea&logoColor=white)](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)   [![Video](https://img.shields.io/badge/YouTube-Tutorial-FF0000?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/watch?v=1Ql4wfH27fM) @@ -29,6 +29,11 @@ Feedback willkommen.
+> [!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. | 🧩 | **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
diff --git a/README.md b/README.md index 7bf3199..b7a9861 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ officially tested or supported. Feedback welcome.   [![Releases](https://img.shields.io/badge/Download-Releases-2EA043?style=for-the-badge&logo=gitea&logoColor=white)](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)   -[![Downloads](https://img.shields.io/badge/Downloads-800%2B-8957E5?style=for-the-badge&logo=gitea&logoColor=white)](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases) +[![Downloads](https://img.shields.io/badge/Downloads-3.1k%2B-8957E5?style=for-the-badge&logo=gitea&logoColor=white)](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)   [![Video](https://img.shields.io/badge/YouTube-Tutorial-FF0000?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/watch?v=1Ql4wfH27fM) @@ -28,6 +28,11 @@ officially tested or supported. Feedback welcome.
+> [!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. | 🧩 | **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
diff --git a/VERSION b/VERSION index ea7550c..35c54f5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.27-nightly5 +0.9.27-nightly6 From f192a9943d2a270b018aadb495e43f694f08524d Mon Sep 17 00:00:00 2001 From: viewit Date: Thu, 25 Jun 2026 23:47:34 +0200 Subject: [PATCH 35/41] =?UTF-8?q?ci:=20apk/wget-Fallback=20f=C3=BCr=20curl?= =?UTF-8?q?=20korrekt=20klammern?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/nightly.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index cd528de..55aeb1c 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -112,10 +112,11 @@ jobs: # curl installieren (BusyBox wget kann kein DELETE/POST mit Headers) if ! command -v curl >/dev/null 2>&1; then - apk add --no-cache curl 2>/dev/null || \ + 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 + "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 From 3531cad0eff068a47fe138814c040763a3e70b9a Mon Sep 17 00:00:00 2001 From: viewit Date: Fri, 26 Jun 2026 22:51:32 +0200 Subject: [PATCH 36/41] nightly: 0.9.27-nightly7 --- .gitea/workflows/nightly.yml | 20 +++++++--- CONTRIBUTING.md | 4 +- README.es.md | 75 ++++++++++++++++-------------------- VERSION | 2 +- kobrax_moonraker_bridge.py | 3 +- web/themes/default/app.js | 1 + 6 files changed, 54 insertions(+), 51 deletions(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index 55aeb1c..863aacb 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -85,17 +85,25 @@ jobs: git config user.name "gitea-actions" git config user.email "actions@it-drui.de" - # Letzten nightly-Tag finden (für Changelog-Range) + # Letzten stabilen Release-Tag finden (v* ohne nightly) als Changelog-Basis git fetch --tags origin 2>/dev/null || true - PREV_TAG=$(git tag | grep '^nightly-' | sort | tail -1) + PREV_TAG=$(git tag | grep -E '^v[0-9]+\.' | sort -V | tail -1) + # Fallback: letzter nightly-Tag + [ -z "$PREV_TAG" ] && PREV_TAG=$(git tag | grep '^nightly-' | sort | tail -1) - # Commits seit letztem Tag sammeln + # Commits seit letztem stabilen Tag — chore/ci-Commits herausfiltern if [ -n "$PREV_TAG" ]; then - COMMITS=$(git log "${PREV_TAG}..HEAD" --pretty=format:"- %s" --no-merges 2>/dev/null || true) + COMMITS=$(git log "${PREV_TAG}..HEAD" --pretty=format:"- %s" --no-merges 2>/dev/null \ + | grep -v '^- chore: nightly' \ + | grep -v '^- ci:' \ + || true) else - COMMITS=$(git log --pretty=format:"- %s" --no-merges -20 2>/dev/null || true) + COMMITS=$(git log --pretty=format:"- %s" --no-merges -30 2>/dev/null \ + | grep -v '^- chore: nightly' \ + | grep -v '^- ci:' \ + || true) fi - [ -z "$COMMITS" ] && COMMITS="- Automatischer Nightly-Build" + [ -z "$COMMITS" ] && COMMITS="- Automatischer Nightly-Build (keine neuen Features seit ${PREV_TAG})" # Body in Temp-Datei (vermeidet YAML-Probleme mit Sonderzeichen wie > oder ```) BODY_FILE=$(mktemp) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b5eb74..f7c3c76 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,13 +63,13 @@ The PR template will be pre-filled. Set the target branch to **`nightly`**. ## Branch model ``` -main ← stable releases only (merged by maintainer) +master ← 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. +Your PR always targets `nightly`. The maintainer periodically merges `nightly → master` for a new stable release. --- diff --git a/README.es.md b/README.es.md index 9cf78f9..ba0d32f 100644 --- a/README.es.md +++ b/README.es.md @@ -14,22 +14,13 @@ ninguna está oficialmente probada ni soportada. Se agradece el feedback. 🇬🇧 English version · 🇩🇪 Deutsche Version - - -> [!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). - -
-
[![Ko-fi](https://img.shields.io/badge/Ko--fi-Apoya%20este%20proyecto-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white)](https://ko-fi.com/viewitde)   [![Releases](https://img.shields.io/badge/Descargar-Lanzamientos-2EA043?style=for-the-badge&logo=gitea&logoColor=white)](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)   -[![Downloads](https://img.shields.io/badge/Descargas-800%2B-8957E5?style=for-the-badge&logo=gitea&logoColor=white)](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases) +[![Downloads](https://img.shields.io/badge/Descargas-3.1k%2B-8957E5?style=for-the-badge&logo=gitea&logoColor=white)](https://gitea.it-drui.de/viewit/KX-Bridge-Release/releases)   [![Video](https://img.shields.io/badge/YouTube-Tutorial-FF0000?style=for-the-badge&logo=youtube&logoColor=white)](https://www.youtube.com/watch?v=1Ql4wfH27fM) @@ -37,6 +28,11 @@ ninguna está oficialmente probada ni soportada. Se agradece el feedback.
+> [!CAUTION] +> **Trabajos de mantenimiento en curso** — 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 las molestias. El manejo, el flujo de trabajo y la mantenibilidad a largo plazo mejorarán considerablemente. +> +> 👉 ¿Quieres contribuir? Por favor lee [CONTRIBUTING.md](CONTRIBUTING.md) primero. + --- ## ✨ Características @@ -53,7 +49,7 @@ ninguna está oficialmente probada ni soportada. Se agradece el feedback. | 🧩 | **Multi-impresora** — múltiples impresoras en **una** instancia del puente, cambia mediante un menú desplegable | | ➕ | **Añade una impresora con un clic** — solo introduce la IP, las credenciales se importan automáticamente | | 🔁 | **Reconexión MQTT robusta** — el puente sobrevive a reinicios nocturnos de la impresora sin reinicio manual | -| 🌐 | **Interfaz multilingüe** — DE / EN / ES / 中文, detecta automáticamente el idioma del navegador | +| 🌐 | **Interfaz multilingüe** — DE / EN / ES / FR / IT / 中文, detecta automáticamente el idioma del navegador | | 🔄 | **Actualización automática** — instala nuevas versiones directamente desde el navegador | | 🧠 | **OrcaSlicer** — protocolo Moonraker completo (HTTP + WebSocket); usa el [build OrcaSlicer-KX](#-slicer-recomendado) para emparejamiento correcto de vendor por ranura | @@ -75,43 +71,17 @@ docker compose up -d **Binario Linux (sin Docker):** ```bash -chmod +x kx-bridge-linux-amd64 && ./kx-bridge-linux-amd64 +chmod +x kx-bridge && ./kx-bridge ``` **EXE Windows (sin Docker):** ``` kx-bridge.exe ``` +> `config\` y `data\` se crean junto al EXE — instalación portátil. -> ⚠️ **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. +> 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 @@ -139,6 +109,11 @@ Impresora → Tipo de conexión **Moonraker** → Host: `http://IP-DEL-PUENTE:71 --- +## 📺 Vídeo tutorial + +[![Configuración y uso de KX-Bridge](https://img.youtube.com/vi/1Ql4wfH27fM/hqdefault.jpg)](https://www.youtube.com/watch?v=1Ql4wfH27fM) + +--- ## 🎨 Slicer recomendado @@ -212,6 +187,24 @@ docker compose up -d --build # recompilar localmente (en lugar de desc --- +## 🌙 Builds nocturnos (Nightly) + +Los builds nocturnos contienen las últimas funciones no publicadas y se generan automáticamente en cada push de desarrollo. +Pueden ser inestables — úsalos para pruebas o acceso anticipado a nuevas funciones. + +```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 +``` + +Volver a la versión estable: + +```bash +docker compose pull && docker compose up -d +``` + +--- + ## 🩹 Solución de problemas
diff --git a/VERSION b/VERSION index 35c54f5..0c51b37 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.27-nightly6 +0.9.27-nightly7 diff --git a/kobrax_moonraker_bridge.py b/kobrax_moonraker_bridge.py index 42bc9e7..dc23f57 100644 --- a/kobrax_moonraker_bridge.py +++ b/kobrax_moonraker_bridge.py @@ -4488,7 +4488,8 @@ class KobraXBridge: for _k in ("PRINTER_IP", "MQTT_PORT", "MQTT_USERNAME", "MQTT_PASSWORD", "MODE_ID", "DEVICE_ID", "DEFAULT_AMS_SLOT", "AUTO_LEVELING", "CAMERA_ON_PRINT", "WEB_UPLOAD_WARNING", "PRINT_START_DIALOG", - "FILE_READY_DIALOG", "BRIDGE_PRINTER_NAME"): + "FILE_READY_DIALOG", "BRIDGE_PRINTER_NAME", + "SPOOLMAN_SERVER", "SPOOLMAN_SYNC_RATE"): os.environ.pop(_k, None) in_docker = os.path.exists("/.dockerenv") or os.environ.get("KX_IN_DOCKER") diff --git a/web/themes/default/app.js b/web/themes/default/app.js index 082bb37..571784c 100644 --- a/web/themes/default/app.js +++ b/web/themes/default/app.js @@ -1654,6 +1654,7 @@ function saveSettings(){ btn.disabled=false; setText('btn-save-settings',T.settings_save); closeSettings(); + _loadSpoolmanStatus(); poll(); },4000); }).catch(function(e){ From 54ce101f99b3ba00d5c8b1a1eef1428fddaf2f4a Mon Sep 17 00:00:00 2001 From: viewit Date: Fri, 26 Jun 2026 23:10:22 +0200 Subject: [PATCH 37/41] ci: Changelog aus CHANGES.md lesen (von release.sh aus Dev-Repo generiert) --- .gitea/workflows/nightly.yml | 38 +++++++++++------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index 863aacb..c7c0de9 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -85,34 +85,18 @@ jobs: git config user.name "gitea-actions" git config user.email "actions@it-drui.de" - # Letzten stabilen Release-Tag finden (v* ohne nightly) als Changelog-Basis - git fetch --tags origin 2>/dev/null || true - PREV_TAG=$(git tag | grep -E '^v[0-9]+\.' | sort -V | tail -1) - # Fallback: letzter nightly-Tag - [ -z "$PREV_TAG" ] && PREV_TAG=$(git tag | grep '^nightly-' | sort | tail -1) - - # Commits seit letztem stabilen Tag — chore/ci-Commits herausfiltern - if [ -n "$PREV_TAG" ]; then - COMMITS=$(git log "${PREV_TAG}..HEAD" --pretty=format:"- %s" --no-merges 2>/dev/null \ - | grep -v '^- chore: nightly' \ - | grep -v '^- ci:' \ - || true) - else - COMMITS=$(git log --pretty=format:"- %s" --no-merges -30 2>/dev/null \ - | grep -v '^- chore: nightly' \ - | grep -v '^- ci:' \ - || true) - fi - [ -z "$COMMITS" ] && COMMITS="- Automatischer Nightly-Build (keine neuen Features seit ${PREV_TAG})" - - # Body in Temp-Datei (vermeidet YAML-Probleme mit Sonderzeichen wie > oder ```) + # Changelog aus CHANGES.md lesen (wird von release.sh aus dem Dev-Repo generiert) 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" + if [ -f CHANGES.md ]; then + cat CHANGES.md > "$BODY_FILE" + else + # Fallback falls CHANGES.md fehlt + printf '## KX-Bridge %s -- Nightly Build\n\n' "$VERSION" > "$BODY_FILE" + printf '[experimentell] Ungetestete Features, nur fuer Tester geeignet.\n\n' >> "$BODY_FILE" + printf '- Automatischer Nightly-Build\n\n---\n\n' >> "$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" + fi # Tag setzen git tag -f "$TAG" From 40f85b1eb648dc77e29800f8c17eef80894faf39 Mon Sep 17 00:00:00 2001 From: viewit Date: Fri, 26 Jun 2026 23:10:27 +0200 Subject: [PATCH 38/41] nightly: 0.9.27-nightly8 --- CHANGES.md | 271 +++++++++++++++++++++++++++++++++++++++++++++++++++++ VERSION | 2 +- 2 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 CHANGES.md diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..960767c --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,271 @@ +## KX-Bridge 0.9.27-nightly8 — Nightly Build + +[experimentell] Ungetestete Features, nur für Tester geeignet. + +### Änderungen seit `v0.9.13` + +- chore: CHANGES.md mit echtem Changelog aus Dev-Repo ins Release-Repo schreiben +- fix(spoolman): SPOOLMAN_* env-Cache bei Restart leeren, Status-Dot nach Save aktualisieren +- chore: README.es.md + CONTRIBUTING.md in release.sh-Sync aufnehmen +- docs: Nightly-Sektion, Wartungshinweis + CONTRIBUTING.md, FR/IT-Sprachen, Downloads-Badge +- fix(update): Nightly-Vergleich auf Versions-String umstellen (statt Datum) +- feat(update): Nightly-Track vom Stable-Track trennen +- fix(release): nightly immer auf nightly-Branch pushen, kein master-Push +- fix(camera): exponentielles Backoff bei ffmpeg-Fehler + /api/camera/reset + ↺-Button +- fix(release): Nightly-Release vom nightly-Branch erlauben +- feat(i18n): fehlende UI-Übersetzungen ergänzt + Keys alphabetisch sortiert (PR #70 @fenopy) +- fix: config/config.ini.example beim Release-Sync mitübertragen (Issue #72) +- feat(ui): Integrationen-Tab in Settings (Spoolman + Obico-Hinweis) +- feat(stack): KobraX Full Stack Compose für Portainer (KX-Bridge + Obico + Spoolman) +- feat(release): Nightly/Stable Release-Workflow mit eigenem Docker-Tag +- feat(spoolman): optionale Spoolman-Filamentverbrauch-Integration (PR #65, @p2l) +- fix(release): Artifact-Download per HTTP statt lokalem Dateipfad +- feat(ui): PR #60 — Print-Workflow + Skip-Dialog Fixes (@gangoke) +- Revert "feat(ui): PR #60 — Print-Workflow + Skip-Dialog Fixes (@gangoke)" +- feat(ui): PR #60 — Print-Workflow + Skip-Dialog Fixes (@gangoke) +- fix(release): PyInstaller-Cache zwischen Builds via extra_git_cmds leeren +- fix(upload): File-Browser nur GCode zulassen (Issue #59) +- fix(mqtt): TLS-Socket Read/Write-Race — Segfault in libcrypto (Issue #53) +- feat(ui): Slot-Mapper Skip-Parität + konkrete Profilnamen (Issue #57) +- feat(print): Auto-Open-Druckdialog + Per-Print-Auto-Leveling + Skip-Fix (PR #56) +- feat(ui): Idle-Buttons im Progress-Card + fix(moonraker): Mobileraker-Kompatibilität (Issue #48, #55) +- feat(filament): Vendor-Visibility-Filter für Profil-Dropdown (Issue #41) +- feat(ui): Settings als Master-Detail-Panel mit Kategorie-Sidebar +- chore: CodeBuilder-URL auf 192.168.178.204 gesetzt +- docs(changelog): 0.9.21 — Android-Kamera, IPv4, Debian-Bookworm, Mobileraker-Fix +- fix(ui): Kamera-Stream auf Android via Snapshot-Polling +- fix(docker): python:3.11-slim-bookworm pinnen (glibc 2.41 GPF auf älteren Kerneln, Issue #53) +- fix(moonraker): configfile extruder-Stub um sensor_type + filament_diameter erweitert (Mobileraker #48) +- fix(release): Artifact-Pfad /app/ → Host-Pfad umschreiben +- docs(changelog): 0.9.20 — French, Z-height, camera fixes, light bug +- fix(docker): ffmpeg ins Image aufnehmen +- chore(i18n): Log-Meldungen auf Englisch umgestellt +- chore(security): 04_configs/decrypted/ zu .gitignore hinzugefügt +- fix(camera): Kamera-Autostart ignoriert Setting nach Bridge-Restart (#50) +- feat(i18n): Französisch hinzugefügt (PR #45, @Nathacks) +- merge: PR #49 — Z-Höhe in der Print-UI anzeigen (@Nathacks) +- Revert "fix(ace): ACE-Slot-Offset um 1 korrigiert (Issue #47)" +- fix(ace): ACE-Slot-Offset um 1 korrigiert (Issue #47) +- fix(compat): configfile-Stub + absolute Webcam-URLs für Mobileraker/Obico +- docs(claude): CLAUDE.md auf aktuellen Stand 0.9.19.1 +- docs(changelog): translate 0.9.19.1 entry to English (CHANGELOG.md was German by mistake) +- fix(spec): VERSION-Datei ins Onefile einbetten — beseitigt vunknown in standalone Binaries +- chore: CHANGELOG 0.9.19.1 + VERSION zurueck fuer release.sh-Bump +- feat(filaments): nur Kobra-X-sichtbare Profile (209 statt 399) +- fix(mqtt): klare Meldung wenn anycubic_slicer.crt/.key fehlen (statt Errno 2) +- chore(gitignore): ignore lokale Build-/Test-/TODO-Pfade +- fix(spec): VERSION-Datei ins Onefile einbetten — beseitigt vunknown in standalone Binaries +- docs(changelog): 0.9.19.1 hotfix entry +- docs(changelog): remove internal test-profile name 'Bert - PLA' +- docs: README — Features für 0.9.17/18/19 + OrcaSlicer-KX-PR-Liste + Kobra S1/S1Max-Hinweis +- chore: 0.9.19 — filament sync matches real presets, custom profile import +- fix(profile-import): User-Profile mit Inherits-Kette parsen +- fix(ui): Slot-Card direkt nach Speichern neu rendern +- fix(filament-sync): Default ist Generic statt Anycubic +- fix(filament-sync): Phantom-Profile raus, Default-Lookup aus JSON +- feat(filament-sync): gate_filament_name im HH-MMU-Objekt befüllen +- feat(filament-sync): sende name + vendor_name pro Lane (PR #13719-kompatibel) +- fix(ace-dryer): setDry fire-and-forget (PR #42 von @gangoke) +- feat(filament): Custom-Profile-Import via Bridge-UI Upload (Issue #41) +- fix(filament): Kobra-X-kompatible IDs in orca_filaments.json + Defaults +- chore: 0.9.18 — Filament set from bridge works, Obico Z-height, slot profile picker fixes +- feat(ui): Slot-Profil-Dropdown auf (vendor,name) + Pause/Resume-Toggle + Vendor-Badge auf Slot-Karte +- feat(bridge): Slot-Profil-Roundtrip + Z-Höhe für Obico + multiColorBox/setInfo via web/printer (Issue #39) +- feat(filament): (vendor,name)-Selector statt filament_id im config-Schema +- feat(i18n): sync Spanish translation refactor from KX-Bridge-Release PR #40 +- fix(mqtt): persistenter Reconnect — Bridge erkennt Drucker-Reboot (Issue #33) +- chore: 0.9.17 — Obico (experimental), multi-language UI, filament-profile sync, H.264 stream +- fix(i18n): GCode-Browser nach Sprachwechsel neu rendern +- fix(upload): trennt Connect-/Send-/Read-Timeout für große GCode-Uploads +- feat(bridge): CameraCache + Translations-Route + h264-Endpoint +- feat(i18n): sync translation refactor from KX-Bridge-Release PR #37 +- fix(temp): tempature/set über web/printer-Topic mit type-Feld +- fix(obico): Klipper-SET_HEATER_TEMPERATURE + korrekter MQTT-Pfad für Temp-Set +- fix(filament-list): erweiterte Vendor-Profile + Dropdown-Refresh bei Material-Wechsel +- feat(ams-sync): User-Override pro Slot für konkrete OrcaSlicer-Filament-Marke +- refactor(release): alle 3 kx-bridge-Targets via CodeBuilder-API +- feat(obico): Layer-aligned Time-Lapse + First-Layer-Scan Pseudo-Macros +- fix(obico): start_time/end_time als Unix-Timestamp (float) statt ISO-String +- feat(obico): Moonraker-Kompatibilität für moonraker-obico-Plugin +- feat: GCode Web-Upload + Download + Verify-Dialog (PR #32 von @gangoke) +- docs: OrcaSlicer-KX (gepatchter Slicer) im README verlinken +- docs: CHANGELOG 0.9.16 — Filament-Sync-Fix ergänzt +- fix: Filament-Sync positionstreu + Happy-Hare-Farbformat (OrcaSlicer) +- docs: CHANGELOG 0.9.16 +- fix: i18n-Lücken im ACE-Dry-Dialog (DE-Übersetzungen + fehlender Key) +- fix: Slicer-Zeit + Thumbnail nach Reload / bei OrcaSlicer-Druck anzeigen +- feat: Settings-Option "Kamera bei Druckstart einschalten" +- feat: Log-Verbesserungen (Dedup-Zähler, Level-Filter, Fehler-Toast, mehr Infos) +- fix: einfarbiger Druck blockiert durch leeren AMS-Slot (OrcaSlicer) +- fix: release.sh resynct Windows-Agent hart vor dem Build +- docs: CHANGELOG 0.9.15 (Issue #29 OrcaSlicer-Webview-Fixes) +- fix: Print-Banner + UI im OrcaSlicer-Webview (Issue #29) +- fix: Upload-Banner nach Druckende ausblenden (Issue #29) +- docs: CHANGELOG 0.9.14 (Theme-System #27, API-Restart #28) +- feat: POST /api/restart Endpoint (PR #28 von @gangoke) +- fix: release.sh stellt kx-bridge.spec im Release-Repo sicher (Windows-Theme-Build) +- chore: kx-bridge.spec von *.spec-ignore ausnehmen +- build: kx-bridge.spec ins Repo aufnehmen (war durch *.spec geignored) +- build: einheitlicher PyInstaller-Spec für Linux + Windows (Theme-Einbettung) +- feat: Theme-System (PR #27 von hirnwunde/Olli) +- docs: klare Update-Warnung (EXE/Docker) im 0.9.13-CHANGELOG +- docs: CHANGELOG 0.9.13 (Self-Update-Fix) +- fix: Self-Update lädt alle Bridge-Module (nicht nur Hauptdatei) +- docs: CHANGELOG 0.9.12 +- docs: Downloads-Badge in READMEs (800+ App-Downloads) +- docs: Home-Assistant-Integration (Community) verlinken (Issue #24) +- revert: Moonraker-UI-Integration entfernen (Mainsail/Fluidd/Mobileraker) +- fix: Mainsail Live-Werte + Pause-State (Issue #25) +- feat: Moonraker-Kompatibilität für Mainsail/Fluidd/Mobileraker (Issue #25) +- refactor: Frontend aus kobrax_moonraker_bridge.py auslagern +- docs: CHANGELOG 0.9.11 (ACE Pro 2 experimentell) + Version-Bump +- feat: ACE Pro 2 Support (PR #26 von gangoke) +- release: LICENSE + NOTICE.md ins Release-Repo syncen +- docs: GPLv3-Lizenz + NOTICE für Anycubic-Drittmaterial +- release: PyBuilder statt GitHub Actions fuer Windows-Build +- release: Versions-Marker-Check entfernt (Binary enthaelt VERSION nicht) +- release: verify_binary nutzt kobrax_moonraker_bridge + VERSION als Marker +- docs: CHANGELOG fuer 0.9.10 + Fokus-Hinweis Stabilisierung +- release: release.sh haerten – Sanity-Checks + Build-Verifikation +- feat: Part-Skip (Exclude Object) – pre-print + mid-print +- release: rohe kx-bridge + kx-bridge.exe Binaries als Release-Assets hochladen +- docs: CHANGELOG für 0.9.9 (bridge_url + Windows config_loader) +- fix: bridge_url leer im Single-Printer-Modus → relative Pfade (Issue #21) +- fix: release.sh kopiert config_loader.py ins Windows-Build-Repo +- docs: README EN/DE getrennt — README.md englisch, README.de.md deutsch (neues Design); release.sh kopiert beide ins Release-Repo +- docs: CHANGELOG für 0.9.8 +- feat: Standalone-Tauglichkeit + Drucker add/remove + leerer Zustand +- feat: Multi-Printer in einer Bridge-Instanz +- feat: T14 Suche/Filter/Sortierung im Browser-Tab + T17 GCode-Filamente +- feat: Drucker-Name editierbar in Settings +- fix: Settings und Update-Check zeigen aktiven Drucker (via _apiUrl) +- fix: i18n für Drucker-Tab (DE/EN) +- feat: Drucker-Tab mit Live-Status aller Instanzen +- feat: Print-History im Browser-Tab (T10) +- fix: CORS-Middleware für alle Endpunkte + bridge_url via request.host +- feat: Slot-Auswahl auch im Upload-Banner (3. Button) +- feat: Filament-Slot-Dialog + POST /kx/print (T06+T08) +- feat: Multi-Printer WebUI + Routing (T05) +- feat: Multi-Printer Config-Format (T04) +- feat: i18n für Browser-Tab (DE/EN) +- chore: Browser-Tab umbenennen (Store → Browser) +- feat: GCode Store Tab in WebUI (T03) +- feat: GCode Store — SQLite-Persistenz, /kx/ API, Thumbnail-Extraktion (T01+T02) +- feat: Happy Hare MMU-Emulation für OrcaSlicer Filament-Sync +- chore: Changelog für 0.9.7 ergänzt +- fix: Upload großer GCode-Dateien (client_max_size 256MB, Socket-Timeout 120s) +- docs: CHANGELOG 0.9.6 nachgetragen (Licht-Sync, Zeit-Cards, Status-Strings, Fixes) +- build: fetch_credentials in release.sh ergänzt, build.sh auf Linux-only bereinigt +- build: sudo durch rm+cp ersetzt für Windows EXE-Copy (Docker root-owned) +- build: fetch_credentials Linux + Windows Binary in build.sh ergänzt +- docs: fetch_credentials als primärer Setup-Weg, Ko-fi Button ergänzt +- feat: fetch_credentials --write-config ersetzt extract_credentials Workflow +- feat: fetch_credentials.py aus PR #19 übernommen +- fix: YouTube Thumbnail auf hqdefault (maxresdefault nicht verfügbar) +- feat: Licht-Status live vom Drucker synchronisieren, Video Tutorial in README +- fix: Zeiten (verstrichen/rest/slicer) werden bei Stopp/Abbruch zurückgesetzt +- fix: file_ready-Banner nach Stopp/Abbruch wird nicht mehr angezeigt +- feat: Progress-Card mit Zeit-Minicards und Layer-Badge, Slicer-Zeit aus GCode-Ende (OrcaSlicer) +- fix: start.sh erstellt config/ und kopiert config.ini.example automatisch (Issue #15) +- feat: Upload-Banner mit Start/Abbrechen, Thumbnail bei Nur-hochladen, Auto-Print-Fix, Log-Scroll-Fix +- fix: auto_print-Reset nach Multipart-Loop entfernt, Log-Scroll-Position bei deaktiviertem Auto-Scroll erhalten +- feat: file_ready Banner – nach Upload-only Druck-Start-Button anzeigen +- fix: Upload-only vs. Upload+Print korrekt unterscheiden +- fix: release.sh – Release-Name einheitlich als 'v0.9.x' (wie bisherige Releases) +- fix: release.sh – CHANGELOG-Body automatisch in Gitea-Release schreiben +- fix: release.sh – stabile Releases nicht als Pre-Release markieren +- fix: i18n – Kamera-Placeholder und Log-Dir-Button übersetzt +- feat: AMS-Slot-Dialog vollständig i18n (DE/EN) +- fix: AMS-Slot onclick – JSON.stringify im HTML-Attribut vermeiden +- feat: AMS-Slot-Editor – Farbe und Material per Klick ändern +- feat: Log-Tab erweitert – volle Payload + TX/RX/Topic-Filter +- docs: CHANGELOG und README auf v0.9.3 aktualisiert +- fix: Update-Check liefert stable statt Dev, VERSION im Dockerfile, Version im Header (#14) +- docs: README.de.md hinzugefügt, README.en.md entfernt – einheitliche Benennung +- docs: CHANGELOG.de.md – deutsche Version des Changelogs +- chore: README und CHANGELOG auf Englisch, UI-Fehlertexte englisch, i18n für connection_error Banner +- chore: 0.9.2 CHANGELOG, toter Code in config_loader entfernt +- chore: Dev-Version auf 0.9.1-dev+1a8ee2a gesetzt +- chore: Release-Binaries und Logs aus Git-Index entfernen, .gitignore erweitert +- fix: Verbindungsfehler in Web-UI als Banner anzeigen (#11); README: OrcaSlicer-URL und extract_credentials-Link präzisiert (#12/#13) +- feat: config.ini statt .env – persistente Settings über docker-compose restart +- docs: CHANGELOG dev – Slicer-Schätzzeit und Update-Dialog Changelog ergänzt +- chore: Dev-Version auf 0.9.1-dev+14cfb27 gesetzt +- feat: Slicer-Schätzzeit im Fortschritts-Panel anzeigen +- revert: Obico-Integration entfernt +- feat(obico): Kamera-Snapshots aktiv zu Obico hochladen +- fix(obico): BRIDGE_URL für Docker-Netzwerk-Kompatibilität +- feat(obico): Layer-Fortschritt in Obico anzeigen +- fix(obico): Snapshot-Endpoint und Layer-Anzeige +- feat(obico): Obico Selfhosted Integration vollständig +- chore: Dev-Version auf 0.9.1-dev+ea587fc gesetzt +- feat: Obico Verbindungsstatus – Badge im Header, connected-Tracking in ObicoAgent +- chore: Dev-Version auf 0.9.1-dev+aca91a5 gesetzt +- feat: Obico Selfhosted Integration – ObicoAgent, Snapshot-Endpoint, Settings-Modal +- chore: Dev-Version auf 0.9.1-dev+5ddfa13 gesetzt +- feat: Changelog aus Gitea-Release im Update-Dialog anzeigen +- chore: Dev-Version auf 0.9.1-dev+60d4287 gesetzt +- docs: CHANGELOG für dev-Branch ergänzt +- chore: Dev-Version auf 0.9.1-dev+374af9e gesetzt +- chore: Log-Buffer auf 500 Einträge vereinheitlicht (Server + Browser) +- chore: Dev-Version auf 0.9.1-dev+1aa8a75 gesetzt +- feat: Log-Tab – Auto-Scroll toggle, Textfilter, Error-Badge im Nav-Tab +- chore: Dev-Version auf 0.9.1-dev+da0bf51 gesetzt +- feat: Server-Log live in Browser-Console (SSE) + Download-Button +- chore: Dev-Version auf 0.9.1-dev+a52f76f gesetzt +- feat: MQTT-Logging – strukturiertes TX/RX-Log mit Duplikat-Filter +- chore: Dev-Version auf 0.9.1-dev+1828eec gesetzt +- feat: Dev-Infrastruktur – Versionsschema 0.9.1-dev+hash, separater Update-Kanal, README.dev.md +- feat: AMS Slot-Auswahl und Auto-Leveling-Option im Settings-Modal (Issue #6) +- chore: _archive/releases Binaries aus Git-Tracking entfernt +- chore: knlogo.png aus Dev-Repo entfernt (nur Release-Repo) +- fix: Bridge zeigt beim Start lokale IP statt 0.0.0.0; README-Hinweis verbessert +- chore: release.sh aktualisiert Version in Release-Repo-READMEs und kopiert Logo +- feat: Logo ins englische README hinzugefügt +- feat: Logo ins README und Projektverzeichnis hinzugefügt +- fix: AMS leere Slots in UI grau darstellen (Issue #5) +- fix: use_ams nur true wenn belegte Slots vorhanden +- fix: leere AMS-Slots beim Druckstart filtern (status!=5), material_type-Feld korrigiert +- fix: apt ffmpeg entfernt, imageio-ffmpeg übernimmt +- fix: Home X/Y entfernt, homeAxis durch homeZ/homeXY ersetzt +- feat: beta14 – imageio-ffmpeg, Z-Buttons fix, Home XY/All, Motors Off +- feat: beta13 – Windows-Fixes, Projektstruktur-Reorganisation +- refactor: Projektstruktur überarbeitet +- feat: Changelog, Tests, start.sh und Dockerfile-Fixes hinzufügen +- docs: Version auf 0.9.1-beta10 korrigieren +- docs: LAN-Modus korrekt beschreiben (Drucker-Menüoption) +- feat: Offline-Start + Verbinden/Trennen-Button im Header +- feat: Release-ZIPs mit Zertifikaten (linux/windows/certs-only) +- fix: PyInstaller frozen-Binary – __file__ durch sys.executable-Pfad ersetzen +- feat: Windows kx-bridge.exe via GitHub Actions bauen +- fix: release.sh kopiert READMEs nicht mehr (Release-Repo hat eigene) +- docs: README.md aktualisiert, README.en.md hinzugefügt, release.sh kopiert READMEs +- docs: Benutzerdoku aktualisiert (v0.9.1-beta4) + englische Version hinzugefügt +- fix: progress und filename auf 0 zurücksetzen bei stoped/canceled +- feat: neue Drucker-Zustände – pausing/paused/resuming/resumed/stopping/stoped +- fix: release.sh – rm Fehler nach Docker-Build abfangen +- fix: release.sh – Windows-EXE Build korrigiert (leeres Workdir, richtiger dist-Pfad) +- feat: release.sh – Binaries bauen + als Gitea-Assets hochladen +- feat: Print-Speed-Card, Übersetzungen vervollständigt +- fix: Temperatursteuerung während des Drucks +- chore: release.sh – Version-Bump und Release-Sync Skript +- feat: UI-Komplettüberarbeitung – Settings, Self-Update, Dashboard, Responsive +- feat: UI-Überarbeitung – KX-Bridge Titel, ViewIT Footer, Kobra-State-Anzeige, Dashboard-Erweiterung +- feat: UI Titel KX-Bridge, Footer ViewIT 2026 +- fix: README Fehlerbehebung – Credential-Rotation entfernt, --verbose Hinweis ergänzt +- fix: sensible Credentials aus README entfernt (Beispielausgabe anonymisiert) +- docs: CLAUDE.md Projektstruktur + Deployment-Status aktualisiert +- feat: Public Beta 0.9.0-beta1 – Docker, Binaries, README, Build-Prozess +- feat: extract_credentials.py + Bridge Tip-Forming & lane_data Endpoint +- feat: AMS Einziehen/Ausziehen UI + feedFilament-Protokoll dokumentiert +- Initial commit: KX-Bridge + Protokoll-Dokumentation + +--- + +### Docker-Image aktualisieren + +```bash +docker compose pull && docker compose up -d +``` + +Image-Tag: `gitea.it-drui.de/viewit/kx-bridge:nightly` diff --git a/VERSION b/VERSION index 0c51b37..e93621f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.27-nightly7 +0.9.27-nightly8 From 67c013f4ffd7270f399bead0f213353d01b558d4 Mon Sep 17 00:00:00 2001 From: viewit Date: Fri, 26 Jun 2026 23:19:02 +0200 Subject: [PATCH 39/41] nightly: 0.9.27-nightly9 --- CHANGES.md | 244 +------------------------------------- VERSION | 2 +- web/themes/default/app.js | 1 + 3 files changed, 5 insertions(+), 242 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 960767c..e383ec9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,9 +1,10 @@ -## KX-Bridge 0.9.27-nightly8 — Nightly Build +## KX-Bridge 0.9.27-nightly9 — Nightly Build [experimentell] Ungetestete Features, nur für Tester geeignet. -### Änderungen seit `v0.9.13` +### Änderungen seit `v0.9.26` +- fix(spoolman): Status-Dot beim Seitenload initialisieren - chore: CHANGES.md mit echtem Changelog aus Dev-Repo ins Release-Repo schreiben - fix(spoolman): SPOOLMAN_* env-Cache bei Restart leeren, Status-Dot nach Save aktualisieren - chore: README.es.md + CONTRIBUTING.md in release.sh-Sync aufnehmen @@ -20,245 +21,6 @@ - feat(release): Nightly/Stable Release-Workflow mit eigenem Docker-Tag - feat(spoolman): optionale Spoolman-Filamentverbrauch-Integration (PR #65, @p2l) - fix(release): Artifact-Download per HTTP statt lokalem Dateipfad -- feat(ui): PR #60 — Print-Workflow + Skip-Dialog Fixes (@gangoke) -- Revert "feat(ui): PR #60 — Print-Workflow + Skip-Dialog Fixes (@gangoke)" -- feat(ui): PR #60 — Print-Workflow + Skip-Dialog Fixes (@gangoke) -- fix(release): PyInstaller-Cache zwischen Builds via extra_git_cmds leeren -- fix(upload): File-Browser nur GCode zulassen (Issue #59) -- fix(mqtt): TLS-Socket Read/Write-Race — Segfault in libcrypto (Issue #53) -- feat(ui): Slot-Mapper Skip-Parität + konkrete Profilnamen (Issue #57) -- feat(print): Auto-Open-Druckdialog + Per-Print-Auto-Leveling + Skip-Fix (PR #56) -- feat(ui): Idle-Buttons im Progress-Card + fix(moonraker): Mobileraker-Kompatibilität (Issue #48, #55) -- feat(filament): Vendor-Visibility-Filter für Profil-Dropdown (Issue #41) -- feat(ui): Settings als Master-Detail-Panel mit Kategorie-Sidebar -- chore: CodeBuilder-URL auf 192.168.178.204 gesetzt -- docs(changelog): 0.9.21 — Android-Kamera, IPv4, Debian-Bookworm, Mobileraker-Fix -- fix(ui): Kamera-Stream auf Android via Snapshot-Polling -- fix(docker): python:3.11-slim-bookworm pinnen (glibc 2.41 GPF auf älteren Kerneln, Issue #53) -- fix(moonraker): configfile extruder-Stub um sensor_type + filament_diameter erweitert (Mobileraker #48) -- fix(release): Artifact-Pfad /app/ → Host-Pfad umschreiben -- docs(changelog): 0.9.20 — French, Z-height, camera fixes, light bug -- fix(docker): ffmpeg ins Image aufnehmen -- chore(i18n): Log-Meldungen auf Englisch umgestellt -- chore(security): 04_configs/decrypted/ zu .gitignore hinzugefügt -- fix(camera): Kamera-Autostart ignoriert Setting nach Bridge-Restart (#50) -- feat(i18n): Französisch hinzugefügt (PR #45, @Nathacks) -- merge: PR #49 — Z-Höhe in der Print-UI anzeigen (@Nathacks) -- Revert "fix(ace): ACE-Slot-Offset um 1 korrigiert (Issue #47)" -- fix(ace): ACE-Slot-Offset um 1 korrigiert (Issue #47) -- fix(compat): configfile-Stub + absolute Webcam-URLs für Mobileraker/Obico -- docs(claude): CLAUDE.md auf aktuellen Stand 0.9.19.1 -- docs(changelog): translate 0.9.19.1 entry to English (CHANGELOG.md was German by mistake) -- fix(spec): VERSION-Datei ins Onefile einbetten — beseitigt vunknown in standalone Binaries -- chore: CHANGELOG 0.9.19.1 + VERSION zurueck fuer release.sh-Bump -- feat(filaments): nur Kobra-X-sichtbare Profile (209 statt 399) -- fix(mqtt): klare Meldung wenn anycubic_slicer.crt/.key fehlen (statt Errno 2) -- chore(gitignore): ignore lokale Build-/Test-/TODO-Pfade -- fix(spec): VERSION-Datei ins Onefile einbetten — beseitigt vunknown in standalone Binaries -- docs(changelog): 0.9.19.1 hotfix entry -- docs(changelog): remove internal test-profile name 'Bert - PLA' -- docs: README — Features für 0.9.17/18/19 + OrcaSlicer-KX-PR-Liste + Kobra S1/S1Max-Hinweis -- chore: 0.9.19 — filament sync matches real presets, custom profile import -- fix(profile-import): User-Profile mit Inherits-Kette parsen -- fix(ui): Slot-Card direkt nach Speichern neu rendern -- fix(filament-sync): Default ist Generic statt Anycubic -- fix(filament-sync): Phantom-Profile raus, Default-Lookup aus JSON -- feat(filament-sync): gate_filament_name im HH-MMU-Objekt befüllen -- feat(filament-sync): sende name + vendor_name pro Lane (PR #13719-kompatibel) -- fix(ace-dryer): setDry fire-and-forget (PR #42 von @gangoke) -- feat(filament): Custom-Profile-Import via Bridge-UI Upload (Issue #41) -- fix(filament): Kobra-X-kompatible IDs in orca_filaments.json + Defaults -- chore: 0.9.18 — Filament set from bridge works, Obico Z-height, slot profile picker fixes -- feat(ui): Slot-Profil-Dropdown auf (vendor,name) + Pause/Resume-Toggle + Vendor-Badge auf Slot-Karte -- feat(bridge): Slot-Profil-Roundtrip + Z-Höhe für Obico + multiColorBox/setInfo via web/printer (Issue #39) -- feat(filament): (vendor,name)-Selector statt filament_id im config-Schema -- feat(i18n): sync Spanish translation refactor from KX-Bridge-Release PR #40 -- fix(mqtt): persistenter Reconnect — Bridge erkennt Drucker-Reboot (Issue #33) -- chore: 0.9.17 — Obico (experimental), multi-language UI, filament-profile sync, H.264 stream -- fix(i18n): GCode-Browser nach Sprachwechsel neu rendern -- fix(upload): trennt Connect-/Send-/Read-Timeout für große GCode-Uploads -- feat(bridge): CameraCache + Translations-Route + h264-Endpoint -- feat(i18n): sync translation refactor from KX-Bridge-Release PR #37 -- fix(temp): tempature/set über web/printer-Topic mit type-Feld -- fix(obico): Klipper-SET_HEATER_TEMPERATURE + korrekter MQTT-Pfad für Temp-Set -- fix(filament-list): erweiterte Vendor-Profile + Dropdown-Refresh bei Material-Wechsel -- feat(ams-sync): User-Override pro Slot für konkrete OrcaSlicer-Filament-Marke -- refactor(release): alle 3 kx-bridge-Targets via CodeBuilder-API -- feat(obico): Layer-aligned Time-Lapse + First-Layer-Scan Pseudo-Macros -- fix(obico): start_time/end_time als Unix-Timestamp (float) statt ISO-String -- feat(obico): Moonraker-Kompatibilität für moonraker-obico-Plugin -- feat: GCode Web-Upload + Download + Verify-Dialog (PR #32 von @gangoke) -- docs: OrcaSlicer-KX (gepatchter Slicer) im README verlinken -- docs: CHANGELOG 0.9.16 — Filament-Sync-Fix ergänzt -- fix: Filament-Sync positionstreu + Happy-Hare-Farbformat (OrcaSlicer) -- docs: CHANGELOG 0.9.16 -- fix: i18n-Lücken im ACE-Dry-Dialog (DE-Übersetzungen + fehlender Key) -- fix: Slicer-Zeit + Thumbnail nach Reload / bei OrcaSlicer-Druck anzeigen -- feat: Settings-Option "Kamera bei Druckstart einschalten" -- feat: Log-Verbesserungen (Dedup-Zähler, Level-Filter, Fehler-Toast, mehr Infos) -- fix: einfarbiger Druck blockiert durch leeren AMS-Slot (OrcaSlicer) -- fix: release.sh resynct Windows-Agent hart vor dem Build -- docs: CHANGELOG 0.9.15 (Issue #29 OrcaSlicer-Webview-Fixes) -- fix: Print-Banner + UI im OrcaSlicer-Webview (Issue #29) -- fix: Upload-Banner nach Druckende ausblenden (Issue #29) -- docs: CHANGELOG 0.9.14 (Theme-System #27, API-Restart #28) -- feat: POST /api/restart Endpoint (PR #28 von @gangoke) -- fix: release.sh stellt kx-bridge.spec im Release-Repo sicher (Windows-Theme-Build) -- chore: kx-bridge.spec von *.spec-ignore ausnehmen -- build: kx-bridge.spec ins Repo aufnehmen (war durch *.spec geignored) -- build: einheitlicher PyInstaller-Spec für Linux + Windows (Theme-Einbettung) -- feat: Theme-System (PR #27 von hirnwunde/Olli) -- docs: klare Update-Warnung (EXE/Docker) im 0.9.13-CHANGELOG -- docs: CHANGELOG 0.9.13 (Self-Update-Fix) -- fix: Self-Update lädt alle Bridge-Module (nicht nur Hauptdatei) -- docs: CHANGELOG 0.9.12 -- docs: Downloads-Badge in READMEs (800+ App-Downloads) -- docs: Home-Assistant-Integration (Community) verlinken (Issue #24) -- revert: Moonraker-UI-Integration entfernen (Mainsail/Fluidd/Mobileraker) -- fix: Mainsail Live-Werte + Pause-State (Issue #25) -- feat: Moonraker-Kompatibilität für Mainsail/Fluidd/Mobileraker (Issue #25) -- refactor: Frontend aus kobrax_moonraker_bridge.py auslagern -- docs: CHANGELOG 0.9.11 (ACE Pro 2 experimentell) + Version-Bump -- feat: ACE Pro 2 Support (PR #26 von gangoke) -- release: LICENSE + NOTICE.md ins Release-Repo syncen -- docs: GPLv3-Lizenz + NOTICE für Anycubic-Drittmaterial -- release: PyBuilder statt GitHub Actions fuer Windows-Build -- release: Versions-Marker-Check entfernt (Binary enthaelt VERSION nicht) -- release: verify_binary nutzt kobrax_moonraker_bridge + VERSION als Marker -- docs: CHANGELOG fuer 0.9.10 + Fokus-Hinweis Stabilisierung -- release: release.sh haerten – Sanity-Checks + Build-Verifikation -- feat: Part-Skip (Exclude Object) – pre-print + mid-print -- release: rohe kx-bridge + kx-bridge.exe Binaries als Release-Assets hochladen -- docs: CHANGELOG für 0.9.9 (bridge_url + Windows config_loader) -- fix: bridge_url leer im Single-Printer-Modus → relative Pfade (Issue #21) -- fix: release.sh kopiert config_loader.py ins Windows-Build-Repo -- docs: README EN/DE getrennt — README.md englisch, README.de.md deutsch (neues Design); release.sh kopiert beide ins Release-Repo -- docs: CHANGELOG für 0.9.8 -- feat: Standalone-Tauglichkeit + Drucker add/remove + leerer Zustand -- feat: Multi-Printer in einer Bridge-Instanz -- feat: T14 Suche/Filter/Sortierung im Browser-Tab + T17 GCode-Filamente -- feat: Drucker-Name editierbar in Settings -- fix: Settings und Update-Check zeigen aktiven Drucker (via _apiUrl) -- fix: i18n für Drucker-Tab (DE/EN) -- feat: Drucker-Tab mit Live-Status aller Instanzen -- feat: Print-History im Browser-Tab (T10) -- fix: CORS-Middleware für alle Endpunkte + bridge_url via request.host -- feat: Slot-Auswahl auch im Upload-Banner (3. Button) -- feat: Filament-Slot-Dialog + POST /kx/print (T06+T08) -- feat: Multi-Printer WebUI + Routing (T05) -- feat: Multi-Printer Config-Format (T04) -- feat: i18n für Browser-Tab (DE/EN) -- chore: Browser-Tab umbenennen (Store → Browser) -- feat: GCode Store Tab in WebUI (T03) -- feat: GCode Store — SQLite-Persistenz, /kx/ API, Thumbnail-Extraktion (T01+T02) -- feat: Happy Hare MMU-Emulation für OrcaSlicer Filament-Sync -- chore: Changelog für 0.9.7 ergänzt -- fix: Upload großer GCode-Dateien (client_max_size 256MB, Socket-Timeout 120s) -- docs: CHANGELOG 0.9.6 nachgetragen (Licht-Sync, Zeit-Cards, Status-Strings, Fixes) -- build: fetch_credentials in release.sh ergänzt, build.sh auf Linux-only bereinigt -- build: sudo durch rm+cp ersetzt für Windows EXE-Copy (Docker root-owned) -- build: fetch_credentials Linux + Windows Binary in build.sh ergänzt -- docs: fetch_credentials als primärer Setup-Weg, Ko-fi Button ergänzt -- feat: fetch_credentials --write-config ersetzt extract_credentials Workflow -- feat: fetch_credentials.py aus PR #19 übernommen -- fix: YouTube Thumbnail auf hqdefault (maxresdefault nicht verfügbar) -- feat: Licht-Status live vom Drucker synchronisieren, Video Tutorial in README -- fix: Zeiten (verstrichen/rest/slicer) werden bei Stopp/Abbruch zurückgesetzt -- fix: file_ready-Banner nach Stopp/Abbruch wird nicht mehr angezeigt -- feat: Progress-Card mit Zeit-Minicards und Layer-Badge, Slicer-Zeit aus GCode-Ende (OrcaSlicer) -- fix: start.sh erstellt config/ und kopiert config.ini.example automatisch (Issue #15) -- feat: Upload-Banner mit Start/Abbrechen, Thumbnail bei Nur-hochladen, Auto-Print-Fix, Log-Scroll-Fix -- fix: auto_print-Reset nach Multipart-Loop entfernt, Log-Scroll-Position bei deaktiviertem Auto-Scroll erhalten -- feat: file_ready Banner – nach Upload-only Druck-Start-Button anzeigen -- fix: Upload-only vs. Upload+Print korrekt unterscheiden -- fix: release.sh – Release-Name einheitlich als 'v0.9.x' (wie bisherige Releases) -- fix: release.sh – CHANGELOG-Body automatisch in Gitea-Release schreiben -- fix: release.sh – stabile Releases nicht als Pre-Release markieren -- fix: i18n – Kamera-Placeholder und Log-Dir-Button übersetzt -- feat: AMS-Slot-Dialog vollständig i18n (DE/EN) -- fix: AMS-Slot onclick – JSON.stringify im HTML-Attribut vermeiden -- feat: AMS-Slot-Editor – Farbe und Material per Klick ändern -- feat: Log-Tab erweitert – volle Payload + TX/RX/Topic-Filter -- docs: CHANGELOG und README auf v0.9.3 aktualisiert -- fix: Update-Check liefert stable statt Dev, VERSION im Dockerfile, Version im Header (#14) -- docs: README.de.md hinzugefügt, README.en.md entfernt – einheitliche Benennung -- docs: CHANGELOG.de.md – deutsche Version des Changelogs -- chore: README und CHANGELOG auf Englisch, UI-Fehlertexte englisch, i18n für connection_error Banner -- chore: 0.9.2 CHANGELOG, toter Code in config_loader entfernt -- chore: Dev-Version auf 0.9.1-dev+1a8ee2a gesetzt -- chore: Release-Binaries und Logs aus Git-Index entfernen, .gitignore erweitert -- fix: Verbindungsfehler in Web-UI als Banner anzeigen (#11); README: OrcaSlicer-URL und extract_credentials-Link präzisiert (#12/#13) -- feat: config.ini statt .env – persistente Settings über docker-compose restart -- docs: CHANGELOG dev – Slicer-Schätzzeit und Update-Dialog Changelog ergänzt -- chore: Dev-Version auf 0.9.1-dev+14cfb27 gesetzt -- feat: Slicer-Schätzzeit im Fortschritts-Panel anzeigen -- revert: Obico-Integration entfernt -- feat(obico): Kamera-Snapshots aktiv zu Obico hochladen -- fix(obico): BRIDGE_URL für Docker-Netzwerk-Kompatibilität -- feat(obico): Layer-Fortschritt in Obico anzeigen -- fix(obico): Snapshot-Endpoint und Layer-Anzeige -- feat(obico): Obico Selfhosted Integration vollständig -- chore: Dev-Version auf 0.9.1-dev+ea587fc gesetzt -- feat: Obico Verbindungsstatus – Badge im Header, connected-Tracking in ObicoAgent -- chore: Dev-Version auf 0.9.1-dev+aca91a5 gesetzt -- feat: Obico Selfhosted Integration – ObicoAgent, Snapshot-Endpoint, Settings-Modal -- chore: Dev-Version auf 0.9.1-dev+5ddfa13 gesetzt -- feat: Changelog aus Gitea-Release im Update-Dialog anzeigen -- chore: Dev-Version auf 0.9.1-dev+60d4287 gesetzt -- docs: CHANGELOG für dev-Branch ergänzt -- chore: Dev-Version auf 0.9.1-dev+374af9e gesetzt -- chore: Log-Buffer auf 500 Einträge vereinheitlicht (Server + Browser) -- chore: Dev-Version auf 0.9.1-dev+1aa8a75 gesetzt -- feat: Log-Tab – Auto-Scroll toggle, Textfilter, Error-Badge im Nav-Tab -- chore: Dev-Version auf 0.9.1-dev+da0bf51 gesetzt -- feat: Server-Log live in Browser-Console (SSE) + Download-Button -- chore: Dev-Version auf 0.9.1-dev+a52f76f gesetzt -- feat: MQTT-Logging – strukturiertes TX/RX-Log mit Duplikat-Filter -- chore: Dev-Version auf 0.9.1-dev+1828eec gesetzt -- feat: Dev-Infrastruktur – Versionsschema 0.9.1-dev+hash, separater Update-Kanal, README.dev.md -- feat: AMS Slot-Auswahl und Auto-Leveling-Option im Settings-Modal (Issue #6) -- chore: _archive/releases Binaries aus Git-Tracking entfernt -- chore: knlogo.png aus Dev-Repo entfernt (nur Release-Repo) -- fix: Bridge zeigt beim Start lokale IP statt 0.0.0.0; README-Hinweis verbessert -- chore: release.sh aktualisiert Version in Release-Repo-READMEs und kopiert Logo -- feat: Logo ins englische README hinzugefügt -- feat: Logo ins README und Projektverzeichnis hinzugefügt -- fix: AMS leere Slots in UI grau darstellen (Issue #5) -- fix: use_ams nur true wenn belegte Slots vorhanden -- fix: leere AMS-Slots beim Druckstart filtern (status!=5), material_type-Feld korrigiert -- fix: apt ffmpeg entfernt, imageio-ffmpeg übernimmt -- fix: Home X/Y entfernt, homeAxis durch homeZ/homeXY ersetzt -- feat: beta14 – imageio-ffmpeg, Z-Buttons fix, Home XY/All, Motors Off -- feat: beta13 – Windows-Fixes, Projektstruktur-Reorganisation -- refactor: Projektstruktur überarbeitet -- feat: Changelog, Tests, start.sh und Dockerfile-Fixes hinzufügen -- docs: Version auf 0.9.1-beta10 korrigieren -- docs: LAN-Modus korrekt beschreiben (Drucker-Menüoption) -- feat: Offline-Start + Verbinden/Trennen-Button im Header -- feat: Release-ZIPs mit Zertifikaten (linux/windows/certs-only) -- fix: PyInstaller frozen-Binary – __file__ durch sys.executable-Pfad ersetzen -- feat: Windows kx-bridge.exe via GitHub Actions bauen -- fix: release.sh kopiert READMEs nicht mehr (Release-Repo hat eigene) -- docs: README.md aktualisiert, README.en.md hinzugefügt, release.sh kopiert READMEs -- docs: Benutzerdoku aktualisiert (v0.9.1-beta4) + englische Version hinzugefügt -- fix: progress und filename auf 0 zurücksetzen bei stoped/canceled -- feat: neue Drucker-Zustände – pausing/paused/resuming/resumed/stopping/stoped -- fix: release.sh – rm Fehler nach Docker-Build abfangen -- fix: release.sh – Windows-EXE Build korrigiert (leeres Workdir, richtiger dist-Pfad) -- feat: release.sh – Binaries bauen + als Gitea-Assets hochladen -- feat: Print-Speed-Card, Übersetzungen vervollständigt -- fix: Temperatursteuerung während des Drucks -- chore: release.sh – Version-Bump und Release-Sync Skript -- feat: UI-Komplettüberarbeitung – Settings, Self-Update, Dashboard, Responsive -- feat: UI-Überarbeitung – KX-Bridge Titel, ViewIT Footer, Kobra-State-Anzeige, Dashboard-Erweiterung -- feat: UI Titel KX-Bridge, Footer ViewIT 2026 -- fix: README Fehlerbehebung – Credential-Rotation entfernt, --verbose Hinweis ergänzt -- fix: sensible Credentials aus README entfernt (Beispielausgabe anonymisiert) -- docs: CLAUDE.md Projektstruktur + Deployment-Status aktualisiert -- feat: Public Beta 0.9.0-beta1 – Docker, Binaries, README, Build-Prozess -- feat: extract_credentials.py + Bridge Tip-Forming & lane_data Endpoint -- feat: AMS Einziehen/Ausziehen UI + feedFilament-Protokoll dokumentiert -- Initial commit: KX-Bridge + Protokoll-Dokumentation --- diff --git a/VERSION b/VERSION index e93621f..c126aa6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.27-nightly8 +0.9.27-nightly9 diff --git a/web/themes/default/app.js b/web/themes/default/app.js index 571784c..db842a5 100644 --- a/web/themes/default/app.js +++ b/web/themes/default/app.js @@ -550,6 +550,7 @@ function ensureAceDryCards(){ // defer until DOM ready window.addEventListener('DOMContentLoaded',function(){ setLanguage(currentLang).catch(function(){}); + _loadSpoolmanStatus(); // Kein Drucker konfiguriert? → direkt in den Drucker-Tab (zeigt "+ Drucker hinzufügen") fetch('/kx/printers').then(function(r){return r.json()}).then(function(d){ if(!d.result||!d.result.length){showPanel('printers');loadPrinterTab();} From ce416f3b9aa4992b39a8abd128818932dce6c05b Mon Sep 17 00:00:00 2001 From: viewit Date: Fri, 26 Jun 2026 23:45:23 +0200 Subject: [PATCH 40/41] ci: release.yml nur noch Docker-Build (Release macht release.sh) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "Create Gitea Release"-Step entfernt → keine doppelte Release-Erstellung mehr (release.sh legt Release + englischen Auto-Changelog + Assets an). - Image-Tag strippt fuehrendes 'v' (VERSION-Datei hat keins). - Tag-Pattern auf 'v*' erweitert (vorher matchte v0.9.x.y-Hotfixes nicht). --- .gitea/workflows/release.yml | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 0bed196..8e58cd9 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -3,7 +3,7 @@ name: Stable Release on: push: tags: - - 'v[0-9]+.[0-9]+.[0-9]+' + - 'v*' jobs: release: @@ -56,9 +56,10 @@ jobs: echo "${{ secrets.REGISTRY_TOKEN }}" | \ docker login gitea.it-drui.de -u "${{ secrets.REGISTRY_USER }}" --password-stdin + # Strip fuehrendes 'v' fuer den Image-Tag (VERSION-Datei hat kein 'v'). - name: Build & push (amd64 + arm64) run: | - VERSION="${GITHUB_REF#refs/tags/}" + VERSION="${GITHUB_REF#refs/tags/v}" docker buildx build \ --platform linux/amd64,linux/arm64 \ --push \ @@ -68,20 +69,6 @@ jobs: -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 - }" + # Hinweis: Das Gitea-Release (inkl. englischem Auto-Changelog + Binaries als + # Assets) erstellt release.sh synchron, da es die lokal via CodeBuilder + # gebauten Binaries direkt hochlaedt. Dieser Workflow baut nur das Docker-Image. From 823cbfe1a9b342a9220ffb1ace1790a782b4f32a Mon Sep 17 00:00:00 2001 From: viewit Date: Sun, 28 Jun 2026 16:21:04 +0200 Subject: [PATCH 41/41] build: sources for v0.9.27-rc1 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c126aa6..703d986 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.27-nightly9 +0.9.27-rc1