|
|
|
@@ -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 }) {
|
|
|
|
|