Dashboard Studio 2.0.0: Admin, Builder and Dashboard screens, roles, Haven

- Home Assistant administrators are admins: Dashboard, Builder and Admin screens
- Everyone else only sees the dashboards an admin chose, always in kiosk mode;
  the server enforces it (saving, people, devices, history are admin-only, and
  the websocket only allows reading states and controlling devices)
- Admin screen: dashboards, people and what each sees (with preview), devices,
  messages to the screens, the Haven app, activity, about
- Haven: new Android app (haven/) that only shows dashboards; replaces the
  Dashboard Studio Android app and signs in through Home Assistant's page
- Screens pick up dashboard and access changes within a minute
- Version history records who made each change; panel visible to all users

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 23:20:55 +01:00
parent 297fb2a7a0
commit 3d958d0411
37 changed files with 1096 additions and 248 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

+66
View File
@@ -0,0 +1,66 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root { color-scheme: dark; }
body { margin: 0; min-height: 100vh; background: linear-gradient(180deg, #17384a 0%, #0d1d29 60%); color: #f4eee4; font: 16px/1.45 system-ui, sans-serif; display: flex; align-items: center; justify-content: center; }
main { width: 100%; max-width: 420px; padding: 28px 22px; box-sizing: border-box; }
img { width: 80px; height: 80px; border-radius: 20px; display: block; margin-bottom: 18px; box-shadow: 0 10px 30px rgba(0, 0, 0, .35); }
.name { font-size: 15px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #f4a531; margin: 0 0 4px; }
h1 { font-size: 26px; margin: 0 0 8px; }
p { margin: 0 0 18px; color: #a9b8c2; }
.msg { background: rgba(255, 69, 58, .15); color: #ffb4ae; padding: 10px 14px; border-radius: 12px; }
.msg:empty { display: none; }
label { display: block; font-size: 14px; font-weight: 600; margin-bottom: 6px; }
input { width: 100%; box-sizing: border-box; font: inherit; color: inherit; background: rgba(255, 255, 255, .07); border: 1px solid rgba(255, 255, 255, .14); border-radius: 12px; padding: 12px 14px; }
input:focus { outline: 2px solid #f4a531; border-color: transparent; }
.hint { font-size: 13px; margin: 8px 0 22px; }
button { width: 100%; font: inherit; font-weight: 700; color: #2b1a02; background: #f4a531; border: 0; border-radius: 12px; padding: 13px; }
button:active { opacity: .85; }
</style>
</head>
<body>
<main>
<img src="icon.png" alt="">
<p class="name">Haven</p>
<h1 data-t="title">Your home’s dashboards</h1>
<p data-t="intro">Enter the address you use to open Home Assistant, then sign in with your own login. You only do this once.</p>
<p class="msg" id="msg"></p>
<form id="f">
<label for="addr" data-t="label">Home Assistant address</label>
<input id="addr" type="text" inputmode="url" autocomplete="url" autocapitalize="off" spellcheck="false" placeholder="http://homeassistant.local:8123" required>
<p class="hint" data-t="hint">Ask whoever set up Home Assistant: it’s on the Admin screen of Dashboard Studio, under Haven app.</p>
<button type="submit" data-t="go">Connect</button>
</form>
</main>
<script>
const NL = {
title: "De dashboards van je huis",
intro: "Vul het adres in waarmee je Home Assistant opent en log in met je eigen account. Dat doe je maar één keer.",
label: "Adres van Home Assistant",
hint: "Vraag het aan wie Home Assistant heeft ingesteld: het staat in Dashboard Studio op het beheerscherm, onder Haven-app.",
go: "Verbinden",
offline: "Home Assistant is niet bereikbaar op dit adres. Controleer het adres en je verbinding.",
notfound: "Dashboard Studio staat niet in Home Assistant. Vraag wie Home Assistant beheert om het te installeren.",
noaddress: "Dashboard Studio is gevonden, maar het adres kon niet worden opgevraagd. Tik in Dashboard Studio op Openen in Haven, of vraag wie Home Assistant beheert.",
error: "Verbinden lukte niet: {0}",
};
const EN = {
offline: "Home Assistant can't be reached at this address. Check the address and your connection.",
notfound: "Dashboard Studio isn't installed in Home Assistant. Ask whoever manages Home Assistant to install it.",
noaddress: "Dashboard Studio was found, but its address couldn't be looked up. Tap Open in Haven in Dashboard Studio, or ask whoever manages Home Assistant.",
error: "Couldn't connect: {0}",
};
const T = navigator.language.startsWith("nl") ? NL : EN;
if (T === NL) document.querySelectorAll("[data-t]").forEach((el) => { el.textContent = NL[el.dataset.t] || el.textContent; });
const app = window.HavenApp;
const why = app ? app.message() : "", detail = app ? app.detail() : "";
if (why) document.getElementById("msg").textContent = (T[why] || T.error).replace("{0}", detail || why);
const addr = document.getElementById("addr");
addr.value = app ? app.address() : "";
document.getElementById("f").onsubmit = (e) => { e.preventDefault(); app.setup(addr.value.trim()); };
</script>
</body>
</html>