Dashboard Studio 1.6.1: show version, load fresh files after updates

- Version shown under About in the dashboard settings, with a link to
  the repository; /api/version reports it
- index.html is never cached and stamps the version on every asset URL,
  so browsers can't keep running old files after an update
- Open screens check the version after reconnecting and every 5 minutes
  and reload into the new version (editing only shows a message);
  pending saves are flushed first

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 22:36:53 +01:00
parent 4187c8f38d
commit fa3e58b747
8 changed files with 69 additions and 5 deletions
+26 -3
View File
@@ -12,6 +12,7 @@ import asyncio
import json
import logging
import os
import re
from pathlib import Path
import aiohttp
@@ -32,7 +33,20 @@ HA_WS_URL = "ws://supervisor/core/websocket"
HA_API_URL = "http://supervisor/core/api"
INGRESS_PROXY_IP = "172.30.32.2"
WEB_DIR = Path(__file__).resolve().parent.parent / "web"
APP_DIR = Path(__file__).resolve().parent.parent
WEB_DIR = APP_DIR / "web"
def read_version():
"""The app version from config.yaml (copied into the image next to server/ and web/)."""
try:
m = re.search(r'^version:\s*"?([^"\n]+)"?', (APP_DIR / "config.yaml").read_text(), re.M)
return m.group(1).strip() if m else "dev"
except OSError:
return "dev"
VERSION = read_version()
DATA_DIR = Path("/data") if Path("/data").is_dir() else Path(__file__).resolve().parent.parent / "data"
STORE_FILE = DATA_DIR / "dashboards.json"
PORT = int(os.environ.get("PORT", "8099"))
@@ -86,7 +100,15 @@ def save_store(data):
async def index(request):
return web.FileResponse(WEB_DIR / "index.html")
# Stamp the version on every asset URL, so an update can never be served from a stale cache.
html = (WEB_DIR / "index.html").read_text()
html = re.sub(r'(static/[\w.-]+\.(?:js|css|png))"', rf'\1?v={VERSION}"', html)
html = html.replace("__VERSION__", VERSION)
return web.Response(text=html, content_type="text/html", headers={"Cache-Control": "no-store"})
async def version(request):
return web.json_response({"version": VERSION}, headers={"Cache-Control": "no-store"})
async def get_dashboards(request):
@@ -191,6 +213,7 @@ async def on_cleanup(app):
def make_app():
app = web.Application(middlewares=[ingress_only], client_max_size=8 * 1024 * 1024)
app.router.add_get("/", index)
app.router.add_get("/api/version", version)
app.router.add_get("/api/dashboards", get_dashboards)
app.router.add_put("/api/dashboards", put_dashboards)
app.router.add_get("/api/camera/{entity_id}", camera)
@@ -206,5 +229,5 @@ def make_app():
if __name__ == "__main__":
if IN_HOME_ASSISTANT and not SUPERVISOR_TOKEN:
LOG.error("SUPERVISOR_TOKEN is missing; the app can't talk to Home Assistant")
LOG.info("Dashboard Studio listening on port %s (data in %s)", PORT, DATA_DIR)
LOG.info("Dashboard Studio %s listening on port %s (data in %s)", VERSION, PORT, DATA_DIR)
web.run_app(make_app(), port=PORT, access_log=None)