Dashboard Studio 2.6.0 and Haven 2.3.0: easier to use on a phone
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+53
-3
@@ -28,6 +28,21 @@
|
||||
.home .x { width: 44px; flex: none; background: transparent; color: #a9b8c2; font-size: 20px; padding: 12px 0; }
|
||||
.add { background: transparent; color: #f4a531; border: 1.5px solid rgba(244, 165, 49, .5); margin-bottom: 22px; }
|
||||
#f.hide { display: none; }
|
||||
.found { margin: 0 0 22px; }
|
||||
.found h2 { font-size: 14px; font-weight: 600; margin: 0 0 8px; }
|
||||
.found .go { display: flex; flex-direction: column; text-align: left; background: rgba(255, 255, 255, .08); color: #f4eee4; padding: 12px 14px; margin-bottom: 8px; }
|
||||
.found .go small { font-weight: 400; color: #a9b8c2; font-size: 13px; }
|
||||
.found .look { display: flex; align-items: center; gap: 10px; font-size: 14px; color: #a9b8c2; margin: 0 0 6px; }
|
||||
.found .look::before { content: ""; width: 14px; height: 14px; border-radius: 50%; border: 2px solid #f4a531; border-right-color: transparent; animation: spin .9s linear infinite; flex: none; }
|
||||
.found .look.done::before { display: none; }
|
||||
.found .look[hidden] { display: none; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
.or { font-size: 14px; color: #a9b8c2; margin: 0 0 12px; }
|
||||
.home.ask { flex-wrap: wrap; background: rgba(255, 69, 58, .12); border-radius: 12px; padding: 12px 14px; }
|
||||
.home.ask p { flex-basis: 100%; margin: 0 0 10px; color: #f4eee4; }
|
||||
.home.ask button { flex: 1; padding: 10px; }
|
||||
.home.ask .keep { background: rgba(255, 255, 255, .1); color: #f4eee4; }
|
||||
.home.ask .rm { background: #ff453a; color: #fff; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -39,6 +54,7 @@
|
||||
<p class="msg" id="msg"></p>
|
||||
<div class="homes" id="homes"></div>
|
||||
<button type="button" class="add" id="add" hidden data-t="add">Add another home</button>
|
||||
<div class="found" id="found" hidden><h2 data-t="foundTitle">On this network</h2><p class="look" id="look" data-t="looking">Looking for Home Assistant…</p><div id="foundList"></div><p class="or" data-t="or">Or enter the address yourself:</p></div>
|
||||
<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>
|
||||
@@ -58,12 +74,19 @@
|
||||
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}",
|
||||
foundTitle: "Op dit netwerk",
|
||||
looking: "Home Assistant zoeken…",
|
||||
notFound: "Niet gevonden op dit netwerk.",
|
||||
or: "Of vul het adres zelf in:",
|
||||
remove: "Verwijderen", keep: "Bewaren", removeQ: "{0} uit Haven verwijderen?",
|
||||
};
|
||||
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}",
|
||||
notFound: "None found on this network.",
|
||||
remove: "Remove", keep: "Keep", removeQ: "Remove {0} from Haven?",
|
||||
};
|
||||
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; });
|
||||
@@ -80,13 +103,40 @@
|
||||
const removeLabel = T === NL ? "Verwijderen" : "Remove";
|
||||
box.innerHTML = homes.map((h) => `<div class="home"><button type="button" class="go ${h.current ? "on" : ""}" data-id="${esc(h.id)}">${esc(h.name)}<small>${esc(h.ha)}</small></button>
|
||||
<button type="button" class="x" data-x="${esc(h.id)}" aria-label="${removeLabel} ${esc(h.name)}">×</button></div>`).join("");
|
||||
const words = (k) => (T[k] || EN[k] || k);
|
||||
box.onclick = (e) => {
|
||||
const go = e.target.closest("[data-id]"), x = e.target.closest("[data-x]");
|
||||
const go = e.target.closest("[data-id]"), x = e.target.closest("[data-x]"), b = e.target.closest("[data-rm], [data-keep]");
|
||||
if (go) app.switchHome(go.dataset.id);
|
||||
if (x && confirm((T === NL ? "Verwijderen: " : "Remove ") + x.closest(".home").querySelector(".go").firstChild.textContent + "?")) app.removeHome(x.dataset.x);
|
||||
// removing asks first, right there in the list
|
||||
if (x) {
|
||||
const row = x.closest(".home"), name = row.querySelector(".go").firstChild.textContent;
|
||||
row.classList.add("ask");
|
||||
row.innerHTML = `<p>${esc(words("removeQ").replace("{0}", name))}</p><button type="button" class="keep" data-keep>${words("keep")}</button><button type="button" class="rm" data-rm="${esc(x.dataset.x)}">${words("remove")}</button>`;
|
||||
}
|
||||
if (b?.dataset.rm) app.removeHome(b.dataset.rm);
|
||||
if (b && "keep" in b.dataset) location.reload();
|
||||
};
|
||||
if (homes.length && homes.some((h) => h.current) && !why) { form.classList.add("hide"); add.hidden = false; }
|
||||
const adding = !(homes.length && homes.some((h) => h.current) && !why);
|
||||
if (!adding) { form.classList.add("hide"); add.hidden = false; }
|
||||
add.onclick = () => app.addHome();
|
||||
|
||||
// Home Assistant announces itself on the home network: list what is found, to tap instead of typing.
|
||||
if (adding && app && app.findHomes) {
|
||||
const wrap = document.getElementById("found"), list = document.getElementById("foundList"), look = document.getElementById("look");
|
||||
const known = new Set(homes.map((h) => h.ha.toLowerCase()));
|
||||
app.findHomes();
|
||||
wrap.hidden = false;
|
||||
let n = 0;
|
||||
const tick = () => {
|
||||
const found = JSON.parse(app.found()).filter((f) => !known.has(f.url.toLowerCase()));
|
||||
list.innerHTML = found.map((f) => `<button type="button" class="go" data-url="${esc(f.url)}">${esc(f.name)}<small>${esc(f.url)}</small></button>`).join("");
|
||||
if (++n < 30) setTimeout(tick, 1000);
|
||||
else { look.classList.add("done"); if (!found.length) look.textContent = words("notFound"); else look.hidden = true; }
|
||||
if (found.length) look.hidden = true;
|
||||
};
|
||||
tick();
|
||||
list.onclick = (e) => { const b = e.target.closest("[data-url]"); if (b) app.setup(b.dataset.url); };
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user