build: sources for v0.9.21

This commit is contained in:
2026-06-14 11:00:32 +02:00
parent 930e3774af
commit 1f300589d1
9 changed files with 69 additions and 52 deletions

View File

@@ -1,5 +1,24 @@
# Changelog
## [0.9.21] 2026-06-14
### Behoben
- **Kamera-Stream auf Android (Chrome / Firefox) nicht sichtbar.** Android-Browser
unterstützen `multipart/x-mixed-replace` (MJPEG) nicht. Die UI erkennt Android
jetzt automatisch und fällt auf Snapshot-Polling mit 5 fps zurück
(`/api/camera/snapshot` alle 200 ms) — keine Server-Änderung nötig.
### Geändert
- Docker-Image auf **Debian 12 (Bookworm)** gepinnt (`python:3.11-slim-bookworm`),
um Kompatibilitätsprobleme mit glibc 2.41 zu vermeiden, die das aktuell von
`python:3.11-slim` gezogene Debian 13 Basis-Image mitbringt.
- MQTT- und HTTP-Verbindungen erzwingen jetzt **IPv4** (`AF_INET`), um
Verbindungsfehler auf Hosts zu verhindern, bei denen der Drucker nur über IPv4
erreichbar ist, das OS aber IPv6 bevorzugt.
- Extruder-Stub in der Moonraker-`configfile`-Antwort enthält jetzt `sensor_type`
und `filament_diameter` — behebt einen Mobileraker-Absturz
(`Null is not a subtype of Object`, Issue #48).
## [0.9.20] 2026-06-08
### Neu

View File

@@ -1,5 +1,24 @@
# Changelog
## [0.9.21] 2026-06-14
### Fixed
- **Camera stream not visible on Android (Chrome / Firefox).** Android
browsers do not support `multipart/x-mixed-replace` (MJPEG). The UI
now detects Android and falls back to snapshot-polling at 5 fps
(`/api/camera/snapshot` every 200 ms) — no server-side change needed.
### Changed
- Docker image now pinned to **Debian 12 (Bookworm)** (`python:3.11-slim-bookworm`)
to avoid glibc 2.41 compatibility issues introduced by the Debian 13
base image that `python:3.11-slim` recently started pulling.
- MQTT and HTTP connections now **force IPv4** (`AF_INET`) to prevent
connection failures on hosts where the printer is only reachable via
IPv4 but the OS prefers IPv6.
- Extruder stub in the Moonraker `configfile` response now includes
`sensor_type` and `filament_diameter` — fixes a Mobileraker crash
(`Null is not a subtype of Object`, issue #48).
## [0.9.20] 2026-06-08
### New

View File

@@ -1,4 +1,4 @@
FROM python:3.11-slim
FROM python:3.11-slim-bookworm
WORKDIR /app

View File

@@ -105,20 +105,6 @@ Drucker → Verbindungstyp **Moonraker** → Host: `http://BRIDGE-IP:7125`
---
## 🐳 Portainer
[`docker-compose.portainer.yml`](docker-compose.portainer.yml) in
**Portainer → Stacks → Add stack → Web editor** einfügen und auf *Deploy* klicken.
Keine Konfiguration nötig — die Bridge startet im Offline-Modus.
**`http://HOST-IP:7125`** öffnen, den **Drucker-Tab** aufrufen und **+ Drucker hinzufügen** klicken.
Nur die Drucker-IP eingeben — Zugangsdaten werden automatisch geholt und gespeichert.
Alle Daten (Konfiguration, GCode-Store, Datenbank) liegen in Named Docker Volumes
(`kx-bridge-config`, `kx-bridge-data`), die Portainer verwaltet.
---
## 📺 Video-Tutorial
[![KX-Bridge Setup & Usage](https://img.youtube.com/vi/1Ql4wfH27fM/hqdefault.jpg)](https://www.youtube.com/watch?v=1Ql4wfH27fM)
@@ -144,7 +130,7 @@ Für sauberen AMS-Filament-Sync gibt es einen **gepatchten OrcaSlicer-Build**:
- Vendor-Match wenn `tray_info_idx` gesetzt ist, das Preset aber inkompatibel
- Zwei-Pass-Suche: erst kompatible Presets, dann alle sichtbaren
**Warum das zusammen wichtig ist:** ohne #13719 landen die AMS-Slots in OrcaSlicer alle auf `Generic PLA` / `Generic PETG`, obwohl die Bridge die konkrete Marke schon mitsendet (`name + vendor_name + gate_filament_name`). Mit dem KX-Build matched OrcaSlicer deine echten User-Presets — auch die, die du via [Eigene OrcaSlicer-Profile importieren](docs/filament-preset-bridge-guide.md) in die Bridge gezogen hast.
**Warum das zusammen wichtig ist:** ohne #13719 landen die AMS-Slots in OrcaSlicer alle auf `Generic PLA` / `Generic PETG`, obwohl die Bridge die konkrete Marke schon mitsendet (`name + vendor_name + gate_filament_name`). Mit dem KX-Build matched OrcaSlicer deine echten User-Presets — auch die, die du via [Eigene OrcaSlicer-Profile importieren](#-features) in die Bridge gezogen hast.
Stock-Upstream-OrcaSlicer funktioniert für Slicing und Drucken weiterhin — nur das Per-Slot-Vendor-Matching beim AMS-Sync fällt dann weg. Material und Farbe pro Slot kannst du auch ohne den KX-Build über die Bridge ans Drucker-Display schreiben (das läuft über MQTT, nicht über den Slicer).

View File

@@ -104,20 +104,6 @@ Printer → Connection type **Moonraker** → Host: `http://BRIDGE-IP:7125`
---
## 🐳 Portainer
Paste [`docker-compose.portainer.yml`](docker-compose.portainer.yml) into
**Portainer → Stacks → Add stack → Web editor** and click *Deploy*.
No configuration needed upfront — the bridge starts in offline mode.
Open **`http://HOST-IP:7125`**, go to the **Printers** tab and click **+ Add printer**.
Enter the printer's IP address — credentials are fetched and saved automatically.
All data (config, GCode store, database) is stored in named Docker volumes
(`kx-bridge-config`, `kx-bridge-data`) managed by Portainer.
---
## 📺 Video Tutorial
[![KX-Bridge Setup & Usage](https://img.youtube.com/vi/1Ql4wfH27fM/hqdefault.jpg)](https://www.youtube.com/watch?v=1Ql4wfH27fM)
@@ -143,7 +129,7 @@ For proper AMS filament-sync we ship a **patched OrcaSlicer build**:
- Vendor match when `tray_info_idx` is set but its preset is incompatible
- Two-pass lookup: first compatible presets, then all visible ones
**Why this matters:** without #13719 the AMS slots in OrcaSlicer all fall back to `Generic PLA` / `Generic PETG` even though the bridge already sends the concrete brand (`name + vendor_name + gate_filament_name`). With the KX build OrcaSlicer matches your actual user presets — including profiles you imported into the bridge via the [Import your own OrcaSlicer profiles](docs/filament-preset-bridge-guide.md) flow.
**Why this matters:** without #13719 the AMS slots in OrcaSlicer all fall back to `Generic PLA` / `Generic PETG` even though the bridge already sends the concrete brand (`name + vendor_name + gate_filament_name`). With the KX build OrcaSlicer matches your actual user presets — including profiles you imported into the bridge via the [Import your own OrcaSlicer profiles](#-features) flow.
Stock upstream OrcaSlicer still works for slicing and printing — you just lose the per-slot brand matching on AMS sync. Slot material + colour can still be pushed bridge → printer either way (that goes over MQTT, not via the slicer).

View File

@@ -1 +1 @@
0.9.20
0.9.21

View File

@@ -167,7 +167,8 @@ class KobraXClient:
ctx.set_ciphers("DEFAULT:@SECLEVEL=0")
ctx.load_cert_chain(CERT_FILE, KEY_FILE)
raw = socket.create_connection((self.host, self.port), timeout=5)
_ai = socket.getaddrinfo(self.host, self.port, socket.AF_INET, socket.SOCK_STREAM)
raw = socket.create_connection(_ai[0][4], timeout=5)
self._sock = ctx.wrap_socket(raw)
log.info("TLS connected cipher=%s", self._sock.cipher()[0])
@@ -596,7 +597,8 @@ class KobraXClient:
# langsamerem WLAN am Drucker dauert das Schieben sonst >30 s und
# würde den Connect-Timeout fälschlich auslösen. Read-Timeout danach
# generös (Drucker verarbeitet die Datei bevor er antwortet).
sock = socket.create_connection((self.host, 18910), timeout=10)
_ai = socket.getaddrinfo(self.host, 18910, socket.AF_INET, socket.SOCK_STREAM)
sock = socket.create_connection(_ai[0][4], timeout=10)
sock.settimeout(None) # blocking während Send
sock.sendall(headers + body)
sock.settimeout(180)

View File

@@ -1906,10 +1906,12 @@ class KobraXBridge:
"square_corner_velocity": 20.0,
},
"extruder": {
"nozzle_diameter": 0.4,
"min_temp": 0,
"max_temp": 320,
"min_extrude_temp": 10,
"nozzle_diameter": 0.4,
"filament_diameter": 1.75,
"sensor_type": "ATC Semitec 104GT-2",
"min_temp": 0,
"max_temp": 320,
"min_extrude_temp": 10,
},
"heater_bed": {
"min_temp": 0,

View File

@@ -7,6 +7,7 @@ var S={nozzle_temp:0,nozzle_target:0,bed_temp:0,bed_target:0,
var tempHistory={n:[],b:[]};
var camOn=false;
var camUserStopped=false; // user stopped camera manually — suppress auto-restart for this print
var _camPollInterval=null; // snapshot-polling interval for Android (no MJPEG support)
var currentStep=1;
var currentPanel='dashboard';
var aceAutoRefillPrefs=(function(){
@@ -1501,23 +1502,24 @@ function camStart(){
img.style.display='none';
sp.style.display='block';
post('/api/camera/start',{}).then(function(){
img.onerror=function(){
sp.style.display='none';
img.style.display='none';
ph.style.display='flex';
camOn=false;
document.getElementById('cam-toggle-btn').textContent=tr('btn_cam_start');
clog(tr('log_error')+' '+tr('cam_stream_unavailable'),'msg-err');
};
img.src='/api/camera/stream?t='+Date.now();
camOn=true;
document.getElementById('cam-toggle-btn').textContent=tr('btn_cam_stop');
clog(tr('log_cam_start'),'msg-ok');
// MJPEG liefert kein onload Spinner nach kurzem Timeout ausblenden
setTimeout(function(){
sp.style.display='none';
img.style.display='block';
},1200);
setTimeout(function(){ sp.style.display='none'; img.style.display='block'; },1200);
if(/Android/i.test(navigator.userAgent)){
// Android browsers don't support multipart/x-mixed-replace (MJPEG) — poll snapshots instead
_camPollInterval=setInterval(function(){ img.src='/api/camera/snapshot?t='+Date.now(); },200);
} else {
img.onerror=function(){
sp.style.display='none';
img.style.display='none';
ph.style.display='flex';
camOn=false;
document.getElementById('cam-toggle-btn').textContent=tr('btn_cam_start');
clog(tr('log_error')+' '+tr('cam_stream_unavailable'),'msg-err');
};
img.src='/api/camera/stream?t='+Date.now();
}
}).catch(function(e){
sp.style.display='none';
ph.style.display='flex';
@@ -1527,6 +1529,7 @@ function camStart(){
function camStop(){
var img=document.getElementById('cam-img');
if(_camPollInterval){clearInterval(_camPollInterval);_camPollInterval=null;}
img.onerror=null; // deregister error handler before clearing src to avoid spurious error toast
post('/api/camera/stop',{}).catch(function(){});
img.src='';