Compare commits

..

1 Commits

Author SHA1 Message Date
Hirnwunde
5b4ba567ab bump theming to 0.9.13 2026-05-21 01:50:56 +02:00
8 changed files with 3003 additions and 107 deletions

4
.gitignore vendored
View File

@@ -1,4 +1,6 @@
.env
config/config.ini
.dev
__pycache__/
*.pyc
build/
@@ -7,5 +9,3 @@ dist/
releases/*/kx-bridge
releases/*/extract_credentials
releases/*/extract_credentials.exe
!kx-bridge.spec

View File

@@ -1,25 +1,5 @@
# Changelog
## [0.9.14] 2026-05-21
### Neu
- **Theme-System (Community-Beitrag von @hirnwunde, PR #27):** Die Web-UI liegt
jetzt in echten Dateien unter `web/themes/<name>/` (`index.html` + `style.css`
+ `app.js`) statt im Python-Quelltext eingebettet. Theme umschalten mit
`--ui-theme <name>`. Für Theme-Autoren gibt es eine dokumentierte Hook-Referenz
(`web/DOC/THEME-CSS-HOOKS.md`, `THEME-JS-ID-HOOKS.md`). Das Default-Theme
enthält die komplette aktuelle UI (ACE2, Objekte überspringen, Filament-Dialog).
Für Nutzer keine Änderung — Binaries/Docker-Image liefern das Theme eingebettet.
- **Neustart über API (Community-Beitrag von @gangoke, PR #28):** neuer Endpoint
`POST /api/restart`, um die Bridge per API neu zu starten — z. B. für einen
Neustart-Button in der Home-Assistant-Integration.
### Intern
- Vereinheitlichter PyInstaller-Build (`kx-bridge.spec`) für Linux, Windows und
Docker — bindet `web/` (Themes) ins Onefile-Binary ein, zur Laufzeit aus
`sys._MEIPASS` gelesen. Theme-Einbettung in Linux-Binary und Windows-EXE verifiziert.
- `data/` in `.gitignore` aufgenommen.
## [0.9.13] 2026-05-20
============================================================

View File

@@ -1,25 +1,5 @@
# Changelog
## [0.9.14] 2026-05-21
### New
- **Theme system (community contribution by @hirnwunde, PR #27):** the web UI now
lives in real files under `web/themes/<name>/` (`index.html` + `style.css` +
`app.js`) instead of being embedded in the Python source. Switch themes with
`--ui-theme <name>`. Theme authors get a documented hook reference
(`web/DOC/THEME-CSS-HOOKS.md`, `THEME-JS-ID-HOOKS.md`). The default theme
carries the full current UI (ACE2, skip objects, filament dialog). No change
for users — the bundled binaries/Docker image ship the theme embedded.
- **Restart over API (community contribution by @gangoke, PR #28):** new
`POST /api/restart` endpoint to restart the bridge remotely — e.g. a restart
button in the Home Assistant integration.
### Internal
- Unified PyInstaller build (`kx-bridge.spec`) for Linux, Windows and Docker —
embeds `web/` (themes) into the one-file binary, read at runtime from
`sys._MEIPASS`. Verified the theme ships in the Linux binary and the Windows EXE.
- `data/` added to `.gitignore`.
## [0.9.13] 2026-05-20
============================================================

View File

@@ -6,7 +6,7 @@ COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY kobrax_moonraker_bridge.py .
COPY web/ ./web/
COPY _web_assets.py .
COPY config_loader.py .
COPY env_loader.py .
COPY kobrax_client.py .

View File

@@ -1 +1 @@
0.9.14
0.9.13

2991
_web_assets.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -49,10 +49,8 @@ import html
# Bei PyInstaller-Binary liegt alles neben sys.executable, sonst neben __file__
_BASE = os.path.dirname(sys.executable) if getattr(sys, "frozen", False) else os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, _BASE)
# Read-Only Web-Assets (Themes) werden im Onefile-Binary via --add-data unter
# sys._MEIPASS entpackt; im Script-/Docker-Modus liegen sie neben dieser Datei.
_WEB_BASE = getattr(sys, "_MEIPASS", _BASE)
from kobrax_client import KobraXClient
from _web_assets import INDEX_HTML
try:
@@ -1888,7 +1886,7 @@ class KobraXBridge:
log.warning("Druckstart: keine Antwort vom Drucker")
def _theme_index_path(self) -> str:
return os.path.join(_WEB_BASE, "web", "themes", self._ui_theme, "index.html")
return os.path.join(_BASE, "web", "themes", self._ui_theme, "index.html")
def _load_index_template_cached(self) -> str:
path = self._theme_index_path()
@@ -1902,7 +1900,7 @@ class KobraXBridge:
return self._index_tpl_cache
def _ui_asset_cache_buster(self) -> str:
base = os.path.join(_WEB_BASE, "web", "themes", self._ui_theme)
base = os.path.join(_BASE, "web", "themes", self._ui_theme)
mt = 0.0
for fn in ("index.html", "style.css", "app.js"):
try:
@@ -2023,7 +2021,7 @@ class KobraXBridge:
ctype = _KX_UI_ASSETS.get(name)
if ctype is None:
raise web.HTTPNotFound()
path = os.path.join(_WEB_BASE, "web", "themes", self._ui_theme, name)
path = os.path.join(_BASE, "web", "themes", self._ui_theme, name)
try:
raw = pathlib.Path(path).read_text(encoding="utf-8")
except OSError:
@@ -2106,12 +2104,6 @@ class KobraXBridge:
log.info("Manuell getrennt")
return web.json_response({"result": "disconnected"})
async def handle_api_restart(self, request):
log.info("Neustart über API angefordert")
response = web.json_response({"status": "restarting"})
asyncio.get_event_loop().call_later(0.3, self._restart_bridge)
return response
async def handle_api_speed(self, request):
try:
body = await request.json()
@@ -2956,13 +2948,12 @@ class KobraXBridge:
except Exception as e:
return web.json_response({"error": str(e)}, status=502)
# Bridge-Python-Module, die das Self-Update mitziehen muss. Wird nur die
# Hauptdatei ersetzt, crasht die neue Version ggf. mit ModuleNotFoundError.
# Hinweis: das Frontend liegt seit dem Theme-System unter web/themes/<name>/
# (keine flache .py mehr); Theme-Dateien werden vom Self-Update derzeit NICHT
# mitgeladen Theme-Änderungen kommen über Docker-Image/Binary-Update.
# Bridge-Python-Module, die das Self-Update mitziehen muss. Die Hauptdatei
# importiert _web_assets (gebündeltes Frontend) etc. wird nur die Hauptdatei
# ersetzt, crasht die neue Version mit ModuleNotFoundError. Daher alle laden.
_UPDATE_FILES = [
"kobrax_moonraker_bridge.py",
"_web_assets.py",
"kobrax_client.py",
"config_loader.py",
"env_loader.py",
@@ -3293,7 +3284,6 @@ def build_app(bridge: KobraXBridge) -> web.Application:
r.add_post("/api/fan", bridge.handle_api_fan)
r.add_post("/api/connect", bridge.handle_api_connect)
r.add_post("/api/disconnect", bridge.handle_api_disconnect)
r.add_post("/api/restart", bridge.handle_api_restart)
r.add_post("/api/speed", bridge.handle_api_speed)
r.add_post("/api/ams/feed", bridge.handle_api_ams_feed)
r.add_post("/api/ams/set_slot", bridge.handle_api_ams_set_slot)

View File

@@ -1,45 +0,0 @@
# PyInstaller-Spec für kx-bridge — plattformneutral (Linux + Windows via PyBuilder).
# Wird relativ zum Repo-Root ausgeführt (`pyinstaller kx-bridge.spec`), wo
# kobrax_moonraker_bridge.py und web/ flach liegen (Release-Repo-Layout).
#
# Bindet das Web-Theme-System (web/themes/<name>/ + web/DOC/) ins Onefile-Binary
# ein → zur Laufzeit über sys._MEIPASS lesbar (_WEB_BASE in der Bridge).
from PyInstaller.utils.hooks import collect_all
datas = [("web", "web")]
binaries = []
hiddenimports = []
# pycryptodome vollständig einsammeln (Krypto für die Drucker-Auth)
_d, _b, _h = collect_all("pycryptodome")
datas += _d
binaries += _b
hiddenimports += _h
a = Analysis(
["kobrax_moonraker_bridge.py"],
pathex=[],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
runtime_hooks=[],
excludes=[],
noarchive=False,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name="kx-bridge",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
console=True,
onefile=True,
)