Dashboard Studio 2.4.1: compact litter box card
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1072,39 +1072,44 @@
|
||||
return { phase: "ready", word: /^(standby|idle|ready|on|off|none|)$/.test(s) ? t("Ready") : st(s), alerts };
|
||||
}
|
||||
|
||||
// Compact: icon, name and controls on top, what it's doing below, today's figures as one short line.
|
||||
// One row high it all goes on one line; tall cards put a ring on top (see the container queries in style.css).
|
||||
function litterBox(c, ha) {
|
||||
if (!c.status) return `<div class="empty">${t("Choose the litter box’s status entity in the inspector.")}</div>`;
|
||||
const { phase, word, alerts } = litterPhase(c, ha);
|
||||
// figures: visits and time today, and the cat's weight; the unit in grey
|
||||
const fig = (eid, lbl, fmt, short = "") => {
|
||||
const val = (eid) => {
|
||||
const s = ha.states[eid];
|
||||
if (!s || ["unknown", "unavailable"].includes(s.state)) return "";
|
||||
const n = Number(s.state), unit = s.attributes.unit_of_measurement || "";
|
||||
const v = fmt ? fmt(n, unit) : `${isFinite(n) ? n.toLocaleString(LOCALE, { maximumFractionDigits: 1 }) : esc(s.state)}${unit ? `<small class="unit"> ${esc(unit)}</small>` : ""}`;
|
||||
return `<div class="lb-fig"><b class="num">${v}</b><span class="lb-l">${esc(lbl)}</span>${short ? `<span class="lb-s">${esc(short)}</span>` : ""}</div>`;
|
||||
return s && !["unknown", "unavailable"].includes(s.state) ? s : null;
|
||||
};
|
||||
// time today: Tuya reports it in seconds or minutes, shown as minutes
|
||||
const mins = (n, unit) => {
|
||||
if (!isFinite(n)) return "–";
|
||||
const m = Math.round(/^s/.test(unit) ? n / 60 : /^h/.test(unit) ? n * 60 : n);
|
||||
return `${m}<small class="unit"> min</small>`;
|
||||
};
|
||||
const figs = [fig(c.visits, t("visits today"), (n) => (isFinite(n) ? String(Math.round(n)) : "–"), t("visits")), fig(c.timeToday, t("in the box today"), mins), fig(c.catWeight, t("cat’s weight"))].join("");
|
||||
const fig = (v, unit, title) => `<span class="lb-fig" title="${esc(title)}"><b class="num">${v}</b> ${esc(unit)}</span>`;
|
||||
const visits = val(c.visits), time = val(c.timeToday), weight = val(c.catWeight);
|
||||
const figs = [
|
||||
visits && isFinite(Number(visits.state)) && fig(Math.round(Number(visits.state)), Number(visits.state) === 1 ? t("visit") : t("visits"), t("Visits today")),
|
||||
// time today: Tuya reports it in seconds or minutes, shown as minutes
|
||||
time && isFinite(Number(time.state)) && (() => {
|
||||
const n = Number(time.state), unit = String(time.attributes.unit_of_measurement || "min").toLowerCase();
|
||||
return fig(Math.round(unit.startsWith("s") ? n / 60 : unit.startsWith("h") ? n * 60 : n), t("min"), t("Time in the box today"));
|
||||
})(),
|
||||
weight && fig(isFinite(Number(weight.state)) ? Number(weight.state).toLocaleString(LOCALE, { maximumFractionDigits: 1 }) : esc(weight.state), weight.attributes.unit_of_measurement || "", t("Cat’s weight")),
|
||||
].filter(Boolean).join("");
|
||||
const device = friendly(ha.states[c.status]).replace(/\s*(status|state)$/i, "");
|
||||
const strip = (txt) => (device && txt.toLowerCase().startsWith(device.toLowerCase() + " ") ? txt.slice(device.length + 1) : txt);
|
||||
const warn = alerts.map((e) => `<span class="appl-fact warn">${Icons.svg("shield")}<span>${esc(cap(strip(friendly(ha.states[e]))))}</span></span>`).join("");
|
||||
const clean = c.cleanBtn && ha.states[c.cleanBtn]
|
||||
? `<button class="lb-clean wbtn" data-act="svc" data-e="${esc(c.cleanBtn)}" data-v="${domainOf(c.cleanBtn) === "script" ? "script.turn_on" : "button.press"}" ${phase === "cleaning" ? "disabled" : ""}>${Icons.svg("sparkles")}<span>${t("Clean now")}</span></button>` : "";
|
||||
? `<button class="lb-clean wbtn" data-act="svc" data-e="${esc(c.cleanBtn)}" data-v="${domainOf(c.cleanBtn) === "script" ? "script.turn_on" : "button.press"}" ${phase === "cleaning" ? "disabled" : ""} title="${t("Clean now")}" aria-label="${t("Clean now")}">${Icons.svg("sparkles")}<span>${t("Clean")}</span></button>` : "";
|
||||
const auto = ha.states[c.autoClean];
|
||||
const autoSw = auto ? `<span class="lb-auto"><span class="lb-auto-l">${t("Auto clean")}</span><button class="light-switch wbtn" role="switch" aria-checked="${isOn(auto)}" data-act="toggle" data-e="${esc(c.autoClean)}" aria-label="${t("Auto clean")}"><span class="switch"></span></button></span>` : "";
|
||||
return `<div class="appl lb phase-${phase}">
|
||||
const autoSw = auto ? `<span class="lb-auto" title="${t("Auto clean")}"><span class="lb-auto-l">${t("Auto")}</span><button class="light-switch wbtn" role="switch" aria-checked="${isOn(auto)}" data-act="toggle" data-e="${esc(c.autoClean)}" aria-label="${t("Auto clean")}"><span class="switch"></span></button></span>` : "";
|
||||
const ico = phase === "attention" ? "shield" : "paw";
|
||||
return `<div class="lb phase-${phase}">
|
||||
<div class="appl-ring lb-ring" aria-hidden="true"><svg viewBox="0 0 100 100"><circle cx="50" cy="50" r="44" class="track"/>${phase === "cleaning" ? `<circle cx="50" cy="50" r="44" class="fill lb-spin" stroke-dasharray="70 207"/>` : ""}</svg>
|
||||
<span class="appl-ico">${Icons.svg(phase === "attention" ? "shield" : "paw")}</span></div>
|
||||
<div class="appl-text">
|
||||
<div class="appl-top"><span class="name">${esc(c.name || t("Litter box"))}</span>${autoSw}</div>
|
||||
<div class="appl-main"><div class="appl-main-text"><span class="appl-big ${phase === "off" ? "appl-off" : ""}">${esc(word)}</span></div>${clean}</div>
|
||||
<span class="appl-ico">${Icons.svg(ico)}</span></div>
|
||||
<div class="lb-body">
|
||||
<span class="lb-ico" aria-hidden="true">${Icons.svg(ico)}</span>
|
||||
<span class="name lb-name">${esc(c.name || t("Litter box"))}</span>
|
||||
${clean || autoSw ? `<div class="lb-acts">${autoSw}${clean}</div>` : ""}
|
||||
<span class="lb-state ${phase === "off" ? "appl-off" : ""}">${esc(word)}</span>
|
||||
${figs ? `<div class="lb-figs">${figs}</div>` : ""}
|
||||
${warn ? `<div class="appl-facts">${warn}</div>` : ""}
|
||||
${warn ? `<div class="lb-warn">${warn}</div>` : ""}
|
||||
</div></div>`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user