build: sources for v0.9.21
This commit is contained in:
@@ -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
|
||||
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM python:3.11-slim
|
||||
FROM python:3.11-slim-bookworm
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
16
README.de.md
16
README.de.md
@@ -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
|
||||
|
||||
[](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).
|
||||
|
||||
|
||||
16
README.md
16
README.md
@@ -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
|
||||
|
||||
[](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).
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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='';
|
||||
|
||||
Reference in New Issue
Block a user