From a3deb33b97fc70bfc51161a8219774ff602dc78a Mon Sep 17 00:00:00 2001 From: viewit Date: Tue, 7 Jul 2026 01:08:41 +0200 Subject: [PATCH] fix(spoolman): status dot showed green when Spoolman was unreachable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /kx/spoolman/status only exposed 'configured' (server URL is set), not actual reachability — health_check() ran once at boot and was only logged, never surfaced to the API or rechecked afterwards. Now the poll loop rechecks reachability every 30s, the status endpoint returns 'reachable', and the frontend dot shows red + '(unreachable)' when Spoolman is configured but not responding. --- kobrax_moonraker_bridge.py | 24 +++++++++++++++++------- web/themes/default/app.js | 9 ++++++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/kobrax_moonraker_bridge.py b/kobrax_moonraker_bridge.py index 414e4d6..2fd8c8a 100644 --- a/kobrax_moonraker_bridge.py +++ b/kobrax_moonraker_bridge.py @@ -944,14 +944,18 @@ class KobraXBridge: client.callbacks["light/report"] = self._on_light client.callbacks["skip/report"] = self._on_skip + # Reachability is rechecked periodically (not just once at boot) so the + # UI status dot reflects the printer's/Spoolman's actual current state + # instead of freezing on the boot-time result. + self._spoolman_reachable: bool = False + self._spoolman_last_health_check: float = 0.0 if self._spoolman: - threading.Thread( - target=lambda: log.info( - f"Spoolman: {'OK' if self._spoolman.health_check() else 'unreachable'} " - f"at {self._spoolman.server_url}" - ), - daemon=True, name="spoolman-health", - ).start() + def _check(): + ok = self._spoolman.health_check() + self._spoolman_reachable = ok + self._spoolman_last_health_check = time.time() + log.info(f"Spoolman: {'OK' if ok else 'unreachable'} at {self._spoolman.server_url}") + threading.Thread(target=_check, daemon=True, name="spoolman-health").start() # ── Spoolman helpers ────────────────────────────────────────────────────── @@ -1042,6 +1046,7 @@ class KobraXBridge: """GET /kx/spoolman/status""" return self._json_cors({ "configured": bool(self._spoolman), + "reachable": self._spoolman_reachable if self._spoolman else False, "server": self._spoolman.server_url if self._spoolman else "", "sync_rate": self._spoolman.sync_rate if self._spoolman else 0, "slot_spools": {str(k): v for k, v in self._spoolman_slot_spools.items()}, @@ -5266,6 +5271,11 @@ class KobraXBridge: else: # No multiColorBox data — still attribute (no transitions to skip) self._spoolman_attribute_tick({}) + # Recheck Spoolman reachability periodically so the UI status + # dot reflects the current state, not just the boot-time result. + if self._spoolman and time.time() - self._spoolman_last_health_check >= 30.0: + self._spoolman_reachable = self._spoolman.health_check() + self._spoolman_last_health_check = time.time() except Exception as e: log.warning(f"Poll error: {e}") # Check whether the printer is really gone diff --git a/web/themes/default/app.js b/web/themes/default/app.js index 158ed7d..84aa661 100644 --- a/web/themes/default/app.js +++ b/web/themes/default/app.js @@ -45,7 +45,7 @@ var ACE_DRY_PRESETS={ }; // Spoolman state -var _spoolmanStatus={configured:false,server:'',sync_rate:0,slot_spools:{}}; +var _spoolmanStatus={configured:false,reachable:false,server:'',sync_rate:0,slot_spools:{}}; var _spoolmanSpools=[]; var _slotSpoolMap={}; // {String(global_index): spoolman_spool_id} — last committed assignment @@ -67,10 +67,12 @@ function _updateSpoolmanStatusDot(){ var dot=document.getElementById('spoolman-status-dot'); var lbl=document.getElementById('spoolman-status-lbl'); if(!dot||!lbl)return; - if(_spoolmanStatus.configured){ + if(!_spoolmanStatus.configured){ + dot.style.color='var(--txt2)';lbl.textContent='nicht konfiguriert'; + } else if(_spoolmanStatus.reachable){ dot.style.color='var(--ok)';lbl.textContent=_spoolmanStatus.server||'verbunden'; } else { - dot.style.color='var(--txt2)';lbl.textContent='nicht konfiguriert'; + dot.style.color='var(--err)';lbl.textContent=(_spoolmanStatus.server||'')+' (nicht erreichbar)'; } } @@ -1965,6 +1967,7 @@ var pollTimer; }); }).catch(function(){}); poll();pollTimer=setInterval(poll,ms); + setInterval(_loadSpoolmanStatus,30000); })(); // ── Print actions ──