Dashboard Studio 1.6.0: app icon, clock weather, icon-only tab rail
- App store icon (icon.png) and favicon, drawn by tools/make_icon.py - Clock can show a weather entity with today's high and low from weather.get_forecasts - Side menu gets an icon-only rail style (now the default): full height, centered icons, active marker, names as hover/focus tooltips; phones keep a labelled bottom tab bar Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -97,6 +97,15 @@
|
||||
});
|
||||
return res?.response || {};
|
||||
}
|
||||
/** Today's and the coming days' forecast, via weather.get_forecasts with return_response. */
|
||||
async forecast(entityId, type = "daily") {
|
||||
await this.ready;
|
||||
const res = await this.send({
|
||||
type: "call_service", domain: "weather", service: "get_forecasts",
|
||||
target: { entity_id: entityId }, service_data: { type }, return_response: true,
|
||||
});
|
||||
return res?.response?.[entityId]?.forecast || [];
|
||||
}
|
||||
async history(entityIds, hours) {
|
||||
await this.ready;
|
||||
const res = await this.send({
|
||||
@@ -134,7 +143,7 @@
|
||||
const DEFAULT_KIOSK = { saver: 0, exitCorner: "top-right", hideCursor: true };
|
||||
|
||||
const newTab = (name, icon = "home") => ({ id: uid(), name, icon, widgets: [] });
|
||||
const newDashboard = (name) => ({ id: uid(), name, theme: { ...DEFAULT_THEME }, grid: { ...DEFAULT_GRID }, kiosk: { ...DEFAULT_KIOSK }, nav: { show: false }, tabs: [newTab(tr("Overview"))] });
|
||||
const newDashboard = (name) => ({ id: uid(), name, theme: { ...DEFAULT_THEME }, grid: { ...DEFAULT_GRID }, kiosk: { ...DEFAULT_KIOSK }, nav: { show: false, style: "icons" }, tabs: [newTab(tr("Overview"))] });
|
||||
|
||||
// Bring dashboards saved by older versions up to the current shape.
|
||||
function migrate(d) {
|
||||
@@ -147,7 +156,7 @@
|
||||
d.theme = { ...DEFAULT_THEME, ...t };
|
||||
d.grid = { ...DEFAULT_GRID, ...(d.grid || {}) };
|
||||
d.kiosk = { ...DEFAULT_KIOSK, ...(d.kiosk || {}) };
|
||||
d.nav = { show: false, ...(d.nav || {}) };
|
||||
d.nav = { show: false, style: "icons", ...(d.nav || {}) };
|
||||
if (!Array.isArray(d.tabs) || !d.tabs.length) d.tabs = [{ ...newTab(tr("Overview")), widgets: d.widgets || [] }];
|
||||
delete d.widgets;
|
||||
return d;
|
||||
@@ -169,8 +178,8 @@
|
||||
const numeric = (e) => !isNaN(Number(ha.states[e].state));
|
||||
|
||||
put("heading", 0, 0, 8, 1, { text: tr("Home"), subtitle: tr("Everything at a glance") });
|
||||
put("clock", 8, 0, 4, 3, {});
|
||||
const weather = first("weather")[0];
|
||||
put("clock", 8, 0, 4, 3, { weather: weather || "" });
|
||||
if (weather) put("weather", 0, 1, 4, 2, { entity: weather });
|
||||
const lights = first("light", 3);
|
||||
if (lights[0]) put("light", 4, 1, 4, 2, { entity: lights[0] });
|
||||
@@ -394,9 +403,11 @@
|
||||
navEl.hidden = !show;
|
||||
if (!show) return;
|
||||
const active = tab().id;
|
||||
navEl.innerHTML = current.tabs.map((t) => `<button class="tabnav-item" data-tab="${t.id}" ${t.id === active ? 'aria-current="page"' : ""}>
|
||||
// "icons": a slim rail; names appear as a tooltip on hover or keyboard focus.
|
||||
navEl.classList.toggle("icons", current.nav.style !== "labels");
|
||||
navEl.innerHTML = current.tabs.map((t) => `<button class="tabnav-item" data-tab="${t.id}" aria-label="${esc(t.name)}" ${t.id === active ? 'aria-current="page"' : ""}>
|
||||
<span class="tabnav-ico">${Icons.has(t.icon) ? Icons.svg(t.icon) : t.icon ? `<span class="glyph">${esc(t.icon)}</span>` : Icons.svg("tile")}</span><span class="tabnav-label">${esc(t.name)}</span></button>`).join("") +
|
||||
(editMode ? `<button class="tabnav-item add" data-tabop="add">${Icons.svg("plus")}<span class="tabnav-label">${tr("Add tab")}</span></button>` : "");
|
||||
(editMode ? `<button class="tabnav-item add" data-tabop="add" aria-label="${tr("Add tab")}">${Icons.svg("plus")}<span class="tabnav-label">${tr("Add tab")}</span></button>` : "");
|
||||
}
|
||||
|
||||
function setTab(id) {
|
||||
@@ -626,6 +637,7 @@
|
||||
<datalist id="dl-icons">${Icons.names.map((n) => `<option value="${n}">`).join("")}</datalist>
|
||||
<div class="section"><div class="section-title">${tr("Tabs")}</div>
|
||||
${fieldHTML({ k: "show", t: "check", l: "Show tabs in a side menu" }, current.nav.show, "nav")}
|
||||
${fieldHTML({ k: "style", t: "seg", l: "Menu style", o: ["icons", "labels"] }, current.nav.style || "icons", "nav")}
|
||||
<div class="tab-list">${current.tabs.map((t, i) => `<div class="tab-item">
|
||||
<span class="icon-prev" aria-hidden="true">${Icons.has(t.icon) ? Icons.svg(t.icon) : esc(t.icon || "")}</span>
|
||||
<input class="input" type="text" data-path="tab" data-tab="${t.id}" data-k="name" value="${esc(t.name)}" aria-label="${tr("Tab name")}">
|
||||
|
||||
Reference in New Issue
Block a user