From 331e8ba9840dbc22985336a9672a102d5f85960c Mon Sep 17 00:00:00 2001 From: fenopy Date: Tue, 28 Jul 2026 08:42:08 -0500 Subject: [PATCH] fix: missing notification function and config example --- config/config.ini.example | 14 ++++++++++++++ config_loader.py | 35 +++++++++++++++++++++++++++++++++++ kobrax_moonraker_bridge.py | 15 +++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/config/config.ini.example b/config/config.ini.example index bea9879..a8de6c8 100644 --- a/config/config.ini.example +++ b/config/config.ini.example @@ -116,3 +116,17 @@ custom_3_duration_sec = 14400 # Wie oft (Sekunden) der Filamentverbrauch während des Drucks gemeldet wird # (0 = nur beim Druckende) # sync_rate = 0 + +[notifications] +# Push-Benachrichtigungen via Apprise (60+ Dienste: discord://, telegram://, +# pover://, gotify://, slack://, ...). Wird normalerweise über die Web-UI +# (Einstellungen → Benachrichtigungen) gepflegt. +# Beispiel: +# url_1 = discord://webhook_id/webhook_token +# events_1 = started,finished,failed,cancelled,paused,progress +# image_1 = false +# enabled_1 = true +# +# Wiederholungsintervall für "progress"-Events (0 = aus) +# notify_every_minutes = 10 +# notify_every_layers = 0 diff --git a/config_loader.py b/config_loader.py index 57fe9ca..401eb54 100644 --- a/config_loader.py +++ b/config_loader.py @@ -194,6 +194,41 @@ def list_printers() -> list[dict]: idx += 1 return printers +def list_notification_urls() -> list[dict]: + """Reads the [notifications] section from config.ini. + + Written by handle_api_settings_post() in kobrax_moonraker_bridge.py as + url_N / events_N / image_N / enabled_N keys (N starting at 1). Stops at + the first missing url_N. + + Returns a list of {"url": str, "events": list[str], "include_image": bool, + "enabled": bool} dicts, in the same shape _notify() expects. + """ + path = _find_config_file() + if not path: + return [] + cfg = configparser.ConfigParser() + cfg.read(path, encoding="utf-8") + section = "notifications" + if not cfg.has_section(section): + return [] + entries: list[dict] = [] + idx = 1 + while cfg.has_option(section, f"url_{idx}"): + url = cfg.get(section, f"url_{idx}", fallback="").strip() + if url: + events_raw = cfg.get(section, f"events_{idx}", fallback="") + events = [e.strip() for e in events_raw.split(",") if e.strip()] + entries.append({ + "url": url, + "events": events, + "include_image": cfg.getboolean(section, f"image_{idx}", fallback=False), + "enabled": cfg.getboolean(section, f"enabled_{idx}", fallback=True), + }) + idx += 1 + return entries + + def _filament_section(printer_id: Optional[str] = None) -> str: """Section name holding a printer's filament-profile mapping. diff --git a/kobrax_moonraker_bridge.py b/kobrax_moonraker_bridge.py index 9edd842..fe0957e 100644 --- a/kobrax_moonraker_bridge.py +++ b/kobrax_moonraker_bridge.py @@ -1595,6 +1595,21 @@ class KobraXBridge: self._spoolman_notify_end() self._current_job_id = "" + # Benachrichtigungen bei Statuswechsel (filename vor Cleanup holen) + if kobra_state and kobra_state != self._prev_kobra_state: + _notif_filename = d.get("filename", self._state.get("filename", "")) + if kobra_state == "printing": + self._notify("started", _notif_filename) + elif kobra_state == "finished": + self._notify("finished", _notif_filename) + elif kobra_state == "failed": + self._notify("failed", _notif_filename) + elif kobra_state in ("stoped", "canceled"): + self._notify("cancelled", _notif_filename) + elif kobra_state in ("pause", "paused"): + self._notify("paused", _notif_filename) + self._prev_kobra_state = kobra_state + # Terminal states (successful finish AND stop/cancel) must leave the # same clean end state - a "finished" print used to only clear # file_ready (Issue #29), leaving progress/filename/duration/layer