/** * TaskMate Routine Card (#676) * * A guided, one-task-at-a-time flow for morning and bedtime routines. The * child card is a checklist — good for scanning, poor for a five-year-old * being walked through getting ready. This shows a single task at a time with * a big Done button, a progress bar, and a celebration at the end. * * Availability comes from the backend's own chore_availability matrix rather * than a re-implementation of the child card's filter, so the weather gate, * reactive deadlines, dependencies and rotation are all honoured for free. * * Version: 1.0.0 */ const LitElement = customElements.get("hui-masonry-view") ? Object.getPrototypeOf(customElements.get("hui-masonry-view")) : Object.getPrototypeOf(customElements.get("hui-view")); const html = LitElement.prototype.html; const css = LitElement.prototype.css; const _safeColor = (c, d) => (typeof c === "string" && /^#[0-9a-fA-F]{3,8}$/.test(c) ? c : d); const TIME_CATEGORIES = ["all", "morning", "afternoon", "evening", "night", "anytime"]; // The card has ONE layout, so it consumes the design tokens directly rather // than carrying a second _renderDesigned() template like the multi-layout // cards do. Every --tmd-* reference below is written with the card's original // value as the fallback, so "classic" — which defines no tokens — renders // exactly as it did before the design system was wired in. const DEFAULT_ACCENT = "#7c3aed"; class TaskMateRoutineCard extends LitElement { static get properties() { return { hass: { type: Object }, config: { type: Object }, _index: { type: Number }, _finished: { type: Boolean }, _busy: { type: Boolean }, }; } constructor() { super(); this._index = 0; this._finished = false; this._busy = false; // Chores completed during THIS run, so the finish screen can total what // was just earned rather than everything the child has done today. this._runCompleted = new Map(); // Tasks the child chose to leave for later. Kept for this run only. this._skipped = new Set(); } setConfig(config) { if (!config.entity) throw new Error("You need to define an entity"); if (!config.child_id) throw new Error("You need to define a child_id"); if (config.time_category && !TIME_CATEGORIES.includes(config.time_category)) { throw new Error(`time_category must be one of: ${TIME_CATEGORIES.join(", ")}`); } this.config = config; } getCardSize() { return 8; } static getStubConfig() { return { entity: "sensor.taskmate_overview", child_id: "", time_category: "morning" }; } shouldUpdate(changedProps) { if (changedProps.has("hass")) { return window.__taskmate_hasChanged ? window.__taskmate_hasChanged(changedProps.get("hass"), this.hass, this.config?.entity) : true; } return true; } _t(key, params) { const fn = window.__taskmate_localize; return fn ? fn(this.hass, key, params) : key; } _attrs() { return (window.__taskmate_attrs && window.__taskmate_attrs(this.hass, this.config.entity)) || this.hass?.states?.[this.config.entity]?.attributes || {}; } /** * Resolve the active design, stamp data-tm-design on the host (which is what * makes the :host-scoped token block apply inside this shadow root), and * settle the header colour. * * The accent is set as an inline property on the host rather than from a *