Compare commits

..

3 Commits

Author SHA1 Message Date
Phil Merricks
de1a5df53c fix: cert check on connect, force IPv4, clean up bridge.sh default IP
- Raise FileNotFoundError with clear message when TLS certs are missing
  rather than a cryptic SSL error
- Use getaddrinfo(AF_INET) before create_connection to force IPv4 and
  avoid dual-stack resolution failures on some networks
- Comment out hardcoded PRINTER_IP in bridge.sh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 17:40:56 +01:00
031e34d8ea merge: PR #42 — Dryer toggle false error (@gangoke)
ACE-Dryer setDry geht jetzt fire-and-forget (timeout=0, kein Response-
Check). Drucker führt den Befehl korrekt aus, aber liefert code:0
statt code:200 — was eine 502-Fehlermeldung in der Bridge-UI auslöste
obwohl der Trockner-Toggle eigentlich funktioniert hat.

Pattern identisch zu setAutoFeed (Z.3161).
2026-06-01 14:28:42 +02:00
Gangoke
fc89dfffa5 fire and forget setDry 2026-05-31 17:27:01 -10:00
4 changed files with 17 additions and 11 deletions

View File

@@ -1 +1 @@
0.9.18
0.9.21

View File

@@ -2,7 +2,7 @@
# Bridge-Manager: start | stop | restart | status | log
SCRIPT="$(dirname "$0")/kobrax_moonraker_bridge.py"
LOGFILE="/tmp/bridge.log"
PRINTER_IP="192.168.178.94"
# PRINTER_IP="192.168.178.94"
case "${1:-restart}" in
start)

View File

@@ -154,13 +154,21 @@ class KobraXClient:
# -- Connection ----------------------------------------------------------
def _do_connect(self):
if not os.path.exists(CERT_FILE) or not os.path.exists(KEY_FILE):
raise FileNotFoundError(
f"TLS-Zertifikate fehlen: anycubic_slicer.crt + anycubic_slicer.key "
f"müssen neben der kx-bridge Binary liegen ({_SCRIPT_DIR}/). "
f"Lade anycubic-certs.zip vom Gitea-Release herunter und entpacke "
f"die Dateien dorthin."
)
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
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])
@@ -589,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

@@ -3085,13 +3085,10 @@ class KobraXBridge:
loop = asyncio.get_event_loop()
def _send():
return self.client.publish("multiColorBox", "setDry", payload, timeout=5)
resp = await loop.run_in_executor(None, _send)
if resp is None:
return web.json_response({"error": "No response from printer"}, status=504)
if int(resp.get("code", 200)) != 200:
return web.json_response({"error": f"Printer rejected command: {resp}"}, status=502)
return self.client.publish("multiColorBox", "setDry", payload, timeout=0)
# Fire-and-forget: setDry ACK arrives via multiColorBox/report callback.
# Waiting for a response on that busy push topic causes false "code:0" rejections.
await loop.run_in_executor(None, _send)
self._state["ace_drying"] = ui_state
self._state_dirty = True