fix: notification images

This commit is contained in:
Matt Pumarlo
2026-06-24 16:08:21 -05:00
parent 0b380f8c2d
commit fc892e03b6

View File

@@ -844,6 +844,7 @@ class KobraXBridge:
self._camera_autostarted: bool = False
self._camera_user_stopped: bool = False # User hat Kamera während Druck manuell gestoppt
self.camera_cache: CameraCache = CameraCache()
self._loop: "asyncio.AbstractEventLoop | None" = None
self._prev_kobra_state: str = ""
self._print_active: bool = False
self._notify_every_minutes: int = 0
@@ -1108,6 +1109,61 @@ class KobraXBridge:
# Notifications (apprise)
# -------------------------------------------------------------------------
async def _capture_notification_image(self, timeout: float = 15.0) -> bytes:
"""Liefert einen aktuellen JPEG-Frame für Bild-Benachrichtigungen.
Falls die Kamera nicht aktiv streamt, wird sie kurz eingeschaltet, ein
Frame abgegriffen und sofern wir sie selbst gestartet haben wieder
ausgeschaltet (toggle on → snapshot → toggle off).
"""
loop = asyncio.get_event_loop()
# 1) Bereits ein frischer Frame im Cache (z. B. WebUI schaut zu)? → direkt nutzen.
if self.camera_cache.latest_jpeg and (time.time() - self.camera_cache.latest_jpeg_ts) < 3.0:
return self.camera_cache.latest_jpeg
started_here = False
try:
# 2) Kamera am Drucker einschalten, falls sie nicht ohnehin läuft.
if not self._camera_autostarted:
try:
await loop.run_in_executor(None, self.client.start_camera)
started_here = True
log.debug("Kamera für Bild-Benachrichtigung temporär gestartet")
except Exception as e:
log.warning(f"Kamera-Start für Benachrichtigung fehlgeschlagen: {e}")
# rtspUrl aktualisieren (kommt im info/report mit, wenn Kamera streamt)
try:
info = await loop.run_in_executor(None, self.client.query_info)
if info:
self._on_info(info)
except Exception:
pass
url = self._state.get("camera_url", "")
if not url:
return b""
self.camera_cache.set_url(url)
await self.camera_cache.ensure_running()
# 3) Auf einen frischen Frame warten (ffmpeg + RTSP-Warmup).
ref_ts = time.time()
deadline = ref_ts + timeout
while time.time() < deadline:
if self.camera_cache.latest_jpeg and self.camera_cache.latest_jpeg_ts >= ref_ts:
return self.camera_cache.latest_jpeg
await asyncio.sleep(0.3)
# Kein frischer Frame notfalls den letzten vorhandenen nehmen.
return self.camera_cache.latest_jpeg
finally:
# 4) Wieder ausschalten, wenn wir sie nur für den Snapshot gestartet
# haben (nicht während eines laufenden Drucks mit Auto-Kamera).
if started_here and not self._camera_autostarted:
try:
await loop.run_in_executor(None, self.client.stop_camera)
log.debug("Kamera nach Bild-Benachrichtigung wieder gestoppt")
except Exception as e:
log.debug(f"Kamera-Stop nach Benachrichtigung fehlgeschlagen: {e}")
def _notify(self, event: str, filename: str = ""):
if self._notifications_paused:
return
@@ -1161,13 +1217,17 @@ class KobraXBridge:
ap2.add(u)
attach = None
tmppath = None
jpeg = self.camera_cache.latest_jpeg
if not jpeg and self.camera_cache._url:
# Camera URL is set but no frame yet — wait briefly (camera may be starting)
deadline = time.monotonic() + 5.0
while not jpeg and time.monotonic() < deadline:
time.sleep(0.3)
jpeg = b""
if self._loop is not None:
try:
fut = asyncio.run_coroutine_threadsafe(
self._capture_notification_image(timeout=15.0), self._loop)
jpeg = fut.result(timeout=30.0) or b""
except Exception as e:
log.warning(f"Kamera-Snapshot für Benachrichtigung fehlgeschlagen: {e}")
jpeg = self.camera_cache.latest_jpeg
else:
jpeg = self.camera_cache.latest_jpeg
if jpeg:
try:
with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as f:
@@ -5494,6 +5554,7 @@ async def run_bridge(args):
client, args=per_args, store=store,
printer_id=pid, all_bridges=all_bridges,
)
bridge._loop = loop
# printer_name aus config.ini übernehmen falls gesetzt
if p.get("name"):
bridge._state["printer_name"] = p["name"]