Dashboard Studio 2.4.2: litter box only shows when the bin is full or something is wrong

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-31 17:15:20 +01:00
parent db6684b787
commit 0eef8593c8
8 changed files with 34 additions and 9 deletions
+2
View File
@@ -242,6 +242,8 @@
// Media cards from before 1.5 kept one player in cfg.entity; move it into the list so it can be edited.
const fix = (w) => {
if (w.type === "media" && w.cfg.entity) { w.cfg.entities = [...new Set([w.cfg.entity, ...(w.cfg.entities || [])])]; delete w.cfg.entity; }
// Litter boxes from 2.4 always showed; from 2.4.2 they only show when the bin is full or something is wrong.
if (w.type === "appliance" && w.cfg.kind === "litterbox" && w.cfg.onlyAttention == null) w.cfg.onlyAttention = true;
(w.cfg.children || []).forEach(fix);
};
d.tabs.forEach((t) => t.widgets.forEach(fix));
+1 -1
View File
@@ -302,7 +302,7 @@
"Clean button": "Schoonmaakknop", "Auto clean switch": "Schakelaar automatisch schoonmaken", "Choose the litter box’s status entity in the inspector.": "Kies de status-entiteit van de kattenbak in de inspector.",
"Emptying": "Aan het legen", "Levelling": "Aan het egaliseren", "Sanitizing": "Aan het ontsmetten", "Sleeping": "Slaapt", "Ready": "Klaar voor gebruik",
"Bin full": "Afvalbak vol", "Check the litter box": "Kijk even bij de kattenbak", "Cat inside": "Kat in de bak", "Needs attention": "Heeft aandacht nodig",
"visits today": "bezoekjes vandaag", "visits": "bezoekjes", "visit": "bezoekje", "Clean": "Schoonmaken", "Auto": "Auto", "in the box today": "in de bak vandaag", "cat’s weight": "gewicht kat", "Clean now": "Nu schoonmaken", "Auto clean": "Automatisch schoonmaken",
"visits today": "bezoekjes vandaag", "visits": "bezoekjes", "visit": "bezoekje", "Only show when the bin is full or something is wrong": "Alleen tonen als de afvalbak vol is of er iets mis is", "Clean": "Schoonmaken", "Auto": "Auto", "in the box today": "in de bak vandaag", "cat’s weight": "gewicht kat", "Clean now": "Nu schoonmaken", "Auto clean": "Automatisch schoonmaken",
"Starts later": "Start later", "Action needed": "Actie nodig", "Error": "Fout", "Stopping": "Stoppen", "Door locked": "Deur vergrendeld",
"Ready at {0}": "Klaar om {0}", "Check the appliance": "Kijk even bij het apparaat", "Something went wrong": "Er ging iets mis", "Stop": "Stop",
"Hide this card when none of its cards are showing": "Verberg deze kaart als geen van de kaarten erin zichtbaar is",
+1 -1
View File
@@ -1610,7 +1610,7 @@ body.in-haven #btn-new { display: none; }
.lb-state { font-size: var(--c-fs-mid); }
.lb-figs { line-height: 22px; height: 22px; overflow: hidden; } /* figures that don't fit wrap out of sight, whole */
}
@container (max-height: 110px) and (max-width: 360px) {
@container (max-height: 110px) and (max-width: 440px) {
.lb-figs { display: none; }
.lb-body { grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "ico state acts"; }
}
+20 -5
View File
@@ -1059,16 +1059,26 @@
// A litter box isn't a machine you start and empty: it waits, cleans itself after a visit, and now and then
// needs its bin emptied. Tuya reports clean / empty / level / sleep / standby / uv; other boxes use similar words.
// An alert is on when it says so ("on", "true") or names the trouble ("full", "fault"), but not "not_full".
const litterAlert = (v) => {
const s = String(v ?? "").toLowerCase();
return ALERT_ON.has(s) || (/full|vol|fault|error|fout|alarm|problem/.test(s) && !/^(not|no|niet)[_ ]/.test(s));
};
function litterPhase(c, ha) {
const s = String(ha.states[c.status]?.state || "").toLowerCase();
const alerts = (c.alerts || []).filter((e) => ALERT_ON.has(String(ha.states[e]?.state).toLowerCase()));
const alerts = (c.alerts || []).filter((e) => litterAlert(ha.states[e]?.state));
const busy = { clean: t("Cleaning…"), cleaning: t("Cleaning…"), empty: t("Emptying"), emptying: t("Emptying"), level: t("Levelling"), leveling: t("Levelling"), uv: t("Sanitizing"), sanitizing: t("Sanitizing") }[s];
if (!c.status || !ha.states[c.status] || ["unavailable", "unknown"].includes(s)) return { phase: "off", word: t("Unavailable"), alerts };
if (/fault|error|fout|full|vol/.test(s)) return { phase: "attention", word: s.includes("full") || s.includes("vol") ? t("Bin full") : t("Check the litter box"), alerts };
if (busy) return { phase: "cleaning", word: busy, alerts };
if (/sleep|slaap/.test(s)) return { phase: "sleep", word: t("Sleeping"), alerts };
if (/cat|occupied|in_use|using/.test(s)) return { phase: "visit", word: t("Cat inside"), alerts };
if (alerts.length) return { phase: "attention", word: t("Needs attention"), alerts };
// name the trouble when an alert says what it is (a full bin), else a general word
if (alerts.length) {
const full = alerts.some((e) => /full|vol|bag|bin|waste|afval/.test(`${e} ${friendly(ha.states[e])}`.toLowerCase()));
return { phase: "attention", word: full ? t("Bin full") : t("Check the litter box"), alerts };
}
return { phase: "ready", word: /^(standby|idle|ready|on|off|none|)$/.test(s) ? t("Ready") : st(s), alerts };
}
@@ -1137,6 +1147,7 @@
{ k: "autoClean", t: "entity", l: "Auto clean switch", domains: ["switch", "input_boolean"], when: litter },
{ k: "alerts", t: "entities", l: "Alerts, e.g. salt or rinse aid low, or a full waste bin", domains: ["sensor", "binary_sensor", "event"] },
{ k: "hideWhenOff", t: "check", l: "Only show while running or finished", d: true, when: notLitter },
{ k: "onlyAttention", t: "check", l: "Only show when the bin is full or something is wrong", d: true, when: litter },
],
// Picking the status fills in the other entities of the same device (Home Connect names them predictably).
autofill: {
@@ -1151,7 +1162,7 @@
return {
kind: "litterbox", visits: find("sensor", /excretion_times|times_day|visits/), timeToday: find("sensor", /excretion_(time_day|duration)|duration/),
catWeight: find("sensor", /cat_weight|weight/), cleanBtn: find("button", /manual_clean|clean/), autoClean: find("switch", /auto_clean/),
alerts: sibs.filter((e) => ["binary_sensor", "sensor"].includes(domainOf(e)) && /full|fault|error|bin|waste|litter_level/.test(e) && e !== eid),
alerts: sibs.filter((e) => ["binary_sensor", "sensor"].includes(domainOf(e)) && /full|fault|error|bin|bag|waste|litter_level/.test(e) && e !== eid),
};
}
return {
@@ -1167,8 +1178,12 @@
entities: (c) => (c.kind === "litterbox" ? [c.status, c.visits, c.timeToday, c.catWeight, c.cleanBtn, c.autoClean, ...(c.alerts || [])]
: [c.status, c.power, c.remaining, c.progress, c.program, c.door, c.energy, c.pauseBtn, c.resumeBtn, c.stopBtn, c.powerSwitch, ...(c.alerts || [])]).filter(Boolean),
// a litter box is always there to look at; it never hides itself
autoHide: (c, { ha }) => c.kind !== "litterbox" && !!c.hideWhenOff && !!(c.status || c.power || c.remaining) && appliancePhase(c, ha) === "off"
&& !(c.alerts || []).some((e) => ALERT_ON.has(String(ha.states[e]?.state).toLowerCase())),
// A litter box only shows when its bin is full or something is wrong (unless that option is off);
// other appliances while running or finished, or while an alert is on.
autoHide: (c, { ha }) => (c.kind === "litterbox"
? c.onlyAttention !== false && !!c.status && litterPhase(c, ha).phase !== "attention"
: !!c.hideWhenOff && !!(c.status || c.power || c.remaining) && appliancePhase(c, ha) === "off"
&& !(c.alerts || []).some((e) => ALERT_ON.has(String(ha.states[e]?.state).toLowerCase()))),
// Two rows high: a progress ring around the appliance on the left, the story on the right.
// Taller cards stack the ring on top (see the container queries in style.css).
render(c, { ha, cache }) {