fix(spoolman): status dot showed green when Spoolman was unreachable

/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.
This commit is contained in:
2026-07-07 01:08:41 +02:00
parent 2e2061a269
commit a3deb33b97
2 changed files with 23 additions and 10 deletions

View File

@@ -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

View File

@@ -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 ──