This commit is contained in:
@@ -812,40 +812,6 @@ class SpoolmanClient:
|
||||
json={"use_length": round(use_length_mm, 2)})
|
||||
|
||||
|
||||
class SpoolmanClient:
|
||||
"""Thin synchronous HTTP client for Spoolman filament tracking.
|
||||
|
||||
Designed to be called from daemon threads (poll loop, _on_print callbacks).
|
||||
Uses requests (already in requirements) so no event-loop dependency.
|
||||
"""
|
||||
|
||||
def __init__(self, server_url: str, sync_rate: int = 0):
|
||||
self.server_url = server_url.rstrip("/")
|
||||
self.sync_rate = sync_rate
|
||||
|
||||
def _req(self, method: str, path: str, **kwargs):
|
||||
import requests
|
||||
r = requests.request(method, f"{self.server_url}{path}", timeout=5, **kwargs)
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
def health_check(self) -> bool:
|
||||
try:
|
||||
self._req("GET", "/api/v1/health")
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def list_spools(self) -> list:
|
||||
return self._req("GET", "/api/v1/spool")
|
||||
|
||||
def use_filament(self, spool_id: int, use_length_mm: float) -> None:
|
||||
"""Report consumed filament length in mm. Spoolman converts to weight
|
||||
using the spool's filament profile density."""
|
||||
self._req("PUT", f"/api/v1/spool/{spool_id}/use",
|
||||
json={"use_length": round(use_length_mm, 2)})
|
||||
|
||||
|
||||
class KobraXBridge:
|
||||
def __init__(self, client: KobraXClient, args=None, store=None, printer_id: str = "1", all_bridges=None):
|
||||
self.client = client
|
||||
@@ -4558,9 +4524,10 @@ class KobraXBridge:
|
||||
|
||||
# ─── Update ──────────────────────────────────────────────────────────────
|
||||
|
||||
STABLE_RELEASE_API = "https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases?limit=1"
|
||||
DEV_RELEASE_API = "https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases?limit=10&pre-release=true"
|
||||
GITEA_RAW_BASE = "https://gitea.it-drui.de/viewit/KX-Bridge-Release/raw/tag"
|
||||
STABLE_RELEASE_API = "https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases?limit=1"
|
||||
NIGHTLY_RELEASE_API = "https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases?limit=5&pre-release=true"
|
||||
DEV_RELEASE_API = "https://gitea.it-drui.de/api/v1/repos/viewit/KX-Bridge-Release/releases?limit=10&pre-release=true"
|
||||
GITEA_RAW_BASE = "https://gitea.it-drui.de/viewit/KX-Bridge-Release/raw/tag"
|
||||
|
||||
def _read_version(self) -> str:
|
||||
# PyInstaller-Onefile entpackt VERSION (per kx-bridge.spec datas) nach
|
||||
@@ -4635,8 +4602,14 @@ class KobraXBridge:
|
||||
|
||||
async def handle_api_update_check(self, request):
|
||||
current = self._read_version()
|
||||
is_nightly = "nightly" in current
|
||||
is_dev = "-dev+" in current
|
||||
api_url = self.DEV_RELEASE_API if is_dev else self.STABLE_RELEASE_API
|
||||
if is_nightly:
|
||||
api_url = self.NIGHTLY_RELEASE_API
|
||||
elif is_dev:
|
||||
api_url = self.DEV_RELEASE_API
|
||||
else:
|
||||
api_url = self.STABLE_RELEASE_API
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(api_url, timeout=aiohttp.ClientTimeout(total=10)) as resp:
|
||||
@@ -4645,14 +4618,44 @@ class KobraXBridge:
|
||||
releases = await resp.json(content_type=None)
|
||||
if not releases:
|
||||
return web.json_response({"error": "Keine Releases gefunden"}, status=404)
|
||||
# Dev: neuestes Release mit "-dev+" im Tag suchen
|
||||
if is_dev:
|
||||
|
||||
if is_nightly:
|
||||
# Neuestes Prerelease mit nightly-Tag suchen
|
||||
nightly_releases = [r for r in releases if r.get("prerelease") and "nightly" in r.get("tag_name", "")]
|
||||
if not nightly_releases:
|
||||
return web.json_response({"error": "Keine Nightly-Releases gefunden"}, status=404)
|
||||
data = nightly_releases[0]
|
||||
tag = data.get("tag_name", "")
|
||||
# Vergleich über Datum im Tag: nightly-YYYYMMDD vs aktuellem Datum in VERSION
|
||||
# Aktuelles Datum aus VERSION extrahieren (Format: 0.9.27-nightlyN oder nightly-YYYYMMDD)
|
||||
import re as _re
|
||||
latest_date = _re.search(r"nightly-(\d{8})", tag)
|
||||
current_date = _re.search(r"nightly-(\d{8})", current)
|
||||
if latest_date and current_date:
|
||||
update_available = latest_date.group(1) > current_date.group(1)
|
||||
else:
|
||||
# Fallback: Tag-Name unterschiedlich = Update verfügbar
|
||||
update_available = tag != current and tag != f"nightly-{current}"
|
||||
latest = tag
|
||||
return web.json_response({
|
||||
"current": current,
|
||||
"latest": latest,
|
||||
"update_available": update_available,
|
||||
"tag": tag,
|
||||
"docker_only": True,
|
||||
"changelog": data.get("body", ""),
|
||||
})
|
||||
elif is_dev:
|
||||
dev_releases = [r for r in releases if "-dev+" in r.get("tag_name", "")]
|
||||
if not dev_releases:
|
||||
return web.json_response({"error": "Keine Dev-Releases gefunden"}, status=404)
|
||||
data = dev_releases[0]
|
||||
else:
|
||||
data = releases[0]
|
||||
# Stable: nur non-prerelease nehmen
|
||||
stable_releases = [r for r in releases if not r.get("prerelease")]
|
||||
if not stable_releases:
|
||||
return web.json_response({"error": "Keine Stable-Releases gefunden"}, status=404)
|
||||
data = stable_releases[0]
|
||||
tag = data.get("tag_name", "")
|
||||
latest = tag.lstrip("v")
|
||||
if is_dev:
|
||||
@@ -4666,6 +4669,7 @@ class KobraXBridge:
|
||||
"update_available": update_available,
|
||||
"tag": tag,
|
||||
"download_url": download_url,
|
||||
"docker_only": False,
|
||||
"changelog": data.get("body", ""),
|
||||
})
|
||||
except Exception as e:
|
||||
@@ -4686,6 +4690,10 @@ class KobraXBridge:
|
||||
async def handle_api_update_apply(self, request):
|
||||
data = await request.json()
|
||||
new_tag = data.get("tag", "")
|
||||
if "nightly" in self._read_version():
|
||||
return web.json_response(
|
||||
{"error": "Nightly-Updates laufen über Docker: "
|
||||
"docker compose pull && docker compose up -d"}, status=400)
|
||||
if getattr(sys, "frozen", False):
|
||||
return web.json_response(
|
||||
{"error": "Self-Update wird im Binary-Modus nicht unterstützt – "
|
||||
|
||||
@@ -1661,21 +1661,33 @@ function saveSettings(){
|
||||
clog('Settings-Fehler: '+e,'msg-err');
|
||||
});
|
||||
}
|
||||
var _updateDockerOnly=false;
|
||||
function checkUpdate(){
|
||||
var sb=document.getElementById('update-status');
|
||||
sb.textContent=T.update_checking;
|
||||
document.getElementById('btn-update-apply').style.display='none';
|
||||
_updateTag='';_updateUrl='';
|
||||
_updateTag='';_updateUrl='';_updateDockerOnly=false;
|
||||
fetch(_apiUrl('/api/update/check')).then(function(r){return r.json()}).then(function(d){
|
||||
if(d.error){sb.textContent=T.update_error+': '+d.error;return;}
|
||||
var cl=document.getElementById('update-changelog');
|
||||
if(d.changelog&&d.changelog.trim()){cl.textContent=d.changelog;cl.style.display='block';}
|
||||
else{cl.style.display='none';}
|
||||
if(d.changelog&&d.changelog.trim()){
|
||||
// Changelog als Markdown-Text (pre-formatiert) anzeigen
|
||||
cl.textContent=d.changelog;cl.style.display='block';
|
||||
} else {cl.style.display='none';}
|
||||
_updateDockerOnly=!!d.docker_only;
|
||||
if(d.update_available){
|
||||
sb.textContent='v'+d.latest+' '+T.update_available;
|
||||
sb.textContent=d.latest+' '+T.update_available;
|
||||
sb.style.color='var(--ok)';
|
||||
_updateTag=d.tag;_updateUrl=d.download_url;
|
||||
document.getElementById('btn-update-apply').style.display='inline-block';
|
||||
_updateTag=d.tag;_updateUrl=d.download_url||'';
|
||||
var btn=document.getElementById('btn-update-apply');
|
||||
if(_updateDockerOnly){
|
||||
btn.textContent=T.update_docker||'docker compose pull';
|
||||
btn.title='docker compose pull && docker compose up -d';
|
||||
} else {
|
||||
btn.textContent=T.update_apply;
|
||||
btn.title='';
|
||||
}
|
||||
btn.style.display='inline-block';
|
||||
} else {
|
||||
sb.textContent=T.update_none;
|
||||
sb.style.color='var(--txt2)';
|
||||
@@ -1683,9 +1695,21 @@ function checkUpdate(){
|
||||
}).catch(function(e){sb.textContent=T.update_error+': '+e;});
|
||||
}
|
||||
function applyUpdate(){
|
||||
if(!_updateUrl)return;
|
||||
var sb=document.getElementById('update-status');
|
||||
var btn=document.getElementById('btn-update-apply');
|
||||
if(_updateDockerOnly){
|
||||
// Nightly: kein Self-Update, Docker-Befehl in Zwischenablage kopieren
|
||||
var cmd='docker compose pull && docker compose up -d';
|
||||
if(navigator.clipboard){
|
||||
navigator.clipboard.writeText(cmd).then(function(){
|
||||
sb.textContent=T.update_docker_copied||'Befehl kopiert: '+cmd;
|
||||
});
|
||||
} else {
|
||||
sb.textContent=cmd;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(!_updateUrl)return;
|
||||
btn.disabled=true;sb.textContent=T.update_applying;
|
||||
post('/api/update/apply',{download_url:_updateUrl,tag:_updateTag}).then(function(){
|
||||
sb.textContent=T.update_restarting;
|
||||
|
||||
@@ -321,6 +321,8 @@
|
||||
"update_available": "verfügbar",
|
||||
"update_check": "Auf Updates prüfen",
|
||||
"update_checking": "Prüfe...",
|
||||
"update_docker": "Befehl kopieren",
|
||||
"update_docker_copied": "Kopiert! Ausführen: docker compose pull && docker compose up -d",
|
||||
"update_error": "Fehler",
|
||||
"update_none": "Bereits aktuell",
|
||||
"update_restarting": "Starte neu..."
|
||||
|
||||
@@ -321,6 +321,8 @@
|
||||
"update_available": "available",
|
||||
"update_check": "Check for Updates",
|
||||
"update_checking": "Checking...",
|
||||
"update_docker": "Copy Command",
|
||||
"update_docker_copied": "Copied! Run: docker compose pull && docker compose up -d",
|
||||
"update_error": "Error",
|
||||
"update_none": "Already up to date",
|
||||
"update_restarting": "Restarting..."
|
||||
|
||||
@@ -321,6 +321,8 @@
|
||||
"update_available": "disponible",
|
||||
"update_check": "Buscar actualizaciones",
|
||||
"update_checking": "Comprobando...",
|
||||
"update_docker": "Copiar comando",
|
||||
"update_docker_copied": "Copiado. Ejecutar: docker compose pull && docker compose up -d",
|
||||
"update_error": "Error",
|
||||
"update_none": "Ya actualizado",
|
||||
"update_restarting": "Reiniciando..."
|
||||
|
||||
@@ -321,6 +321,8 @@
|
||||
"update_available": "disponible",
|
||||
"update_check": "Vérifier les mises à jour",
|
||||
"update_checking": "Vérification…",
|
||||
"update_docker": "Copier la commande",
|
||||
"update_docker_copied": "Copié ! Exécuter : docker compose pull && docker compose up -d",
|
||||
"update_error": "Erreur",
|
||||
"update_none": "Déjà à jour",
|
||||
"update_restarting": "Redémarrage…"
|
||||
|
||||
@@ -321,6 +321,8 @@
|
||||
"update_available": "disponibile",
|
||||
"update_check": "Controlla aggiornamenti",
|
||||
"update_checking": "Verifica in corso...",
|
||||
"update_docker": "Copia comando",
|
||||
"update_docker_copied": "Copiato! Eseguire: docker compose pull && docker compose up -d",
|
||||
"update_error": "Errore",
|
||||
"update_none": "Già aggiornato",
|
||||
"update_restarting": "Riavvio in corso..."
|
||||
|
||||
@@ -321,6 +321,8 @@
|
||||
"update_available": "可用",
|
||||
"update_check": "检查更新",
|
||||
"update_checking": "检查中...",
|
||||
"update_docker": "复制命令",
|
||||
"update_docker_copied": "已复制!执行:docker compose pull && docker compose up -d",
|
||||
"update_error": "错误",
|
||||
"update_none": "已是最新版本",
|
||||
"update_restarting": "重启中..."
|
||||
|
||||
Reference in New Issue
Block a user