diff --git a/custom_components/maintenance_supporter/__init__.py b/custom_components/maintenance_supporter/__init__.py index 7fd3ce63..67afa2a1 100644 --- a/custom_components/maintenance_supporter/__init__.py +++ b/custom_components/maintenance_supporter/__init__.py @@ -53,6 +53,7 @@ from .const import ( CONF_BUDGET_YEARLY, CONF_GROUPS, CONF_INSTALL_ASSIST_SENTENCES, + CONF_NOTIFY_SERVICE, CONF_OBJECT, CONF_PANEL_ENABLED, CONF_SHOPPING_LIST_ENTITY, @@ -1150,17 +1151,25 @@ async def _async_setup_shared(hass: HomeAssistant) -> bool: from .helpers.entity_rename import rewrite_object, rewrite_store, rewrite_tasks - # v2.67: the global shopping-list target is an entity reference too. + # v2.67: the global shopping-list target is an entity reference too — + # and so is a notify ENTITY picked as the notification target (a + # renamed one used to leave notifications to a repair message; a + # legacy notify service is no entity and never renamed). gentry = get_global_entry(hass) - if gentry is not None and gentry.options.get(CONF_SHOPPING_LIST_ENTITY) == old_eid: - hass.config_entries.async_update_entry( - gentry, options={**gentry.options, CONF_SHOPPING_LIST_ENTITY: new_eid} - ) - from .shopping_sync import SHOPPING_SYNC_KEY + if gentry is not None: + renamed = { + key: new_eid + for key in (CONF_SHOPPING_LIST_ENTITY, CONF_NOTIFY_SERVICE) + if gentry.options.get(key, gentry.data.get(key)) == old_eid + } + if renamed: + hass.config_entries.async_update_entry(gentry, options={**gentry.options, **renamed}) + if CONF_SHOPPING_LIST_ENTITY in renamed: + from .shopping_sync import SHOPPING_SYNC_KEY - sync = hass.data.get(DOMAIN, {}).get(SHOPPING_SYNC_KEY) - if sync is not None: - await sync.async_handle_rename(old_eid, new_eid) + sync = hass.data.get(DOMAIN, {}).get(SHOPPING_SYNC_KEY) + if sync is not None: + await sync.async_handle_rename(old_eid, new_eid) for ce in hass.config_entries.async_entries(DOMAIN): if ce.unique_id == GLOBAL_UNIQUE_ID: diff --git a/custom_components/maintenance_supporter/coordinator.py b/custom_components/maintenance_supporter/coordinator.py index bb1b4ad7..0130c9c8 100644 --- a/custom_components/maintenance_supporter/coordinator.py +++ b/custom_components/maintenance_supporter/coordinator.py @@ -1709,7 +1709,8 @@ class MaintenanceCoordinator(DataUpdateCoordinator[dict[str, Any]]): dynamic: dict[str, Any], static: dict[str, Any], ) -> None: - """Put a task back the way it was before one voice action (undo). + """Put a task back the way it was before one action (undo — by voice, + or the panel's and the card's Undo after Complete). ``dynamic`` maps Store fields to their earlier values (``None`` = the field was absent); ``static`` does the same for the entry-level @@ -1730,8 +1731,11 @@ class MaintenanceCoordinator(DataUpdateCoordinator[dict[str, Any]]): write_task(self.hass, self.entry, task_id, current) # The undone completion must not swallow the real one that follows it # as a double tap, nor hold the triggers in their post-completion - # cooldown. + # cooldown. That holds for a back-dated one too: undone and sent again + # with the same moment (to add the forgotten note), it was dropped as + # a "double submit" for 30 s. self._recent_manual_completions.pop(task_id, None) + self._recent_backfills = {key: ts for key, ts in self._recent_backfills.items() if key[0] != task_id} self._recently_completed.pop(task_id, None) self._completion_cooldown.discard(task_id) await self._store.async_save() diff --git a/custom_components/maintenance_supporter/frontend-src/__tests__/calendar-bucket.test.ts b/custom_components/maintenance_supporter/frontend-src/__tests__/calendar-bucket.test.ts index dd9b4179..38cc9592 100644 --- a/custom_components/maintenance_supporter/frontend-src/__tests__/calendar-bucket.test.ts +++ b/custom_components/maintenance_supporter/frontend-src/__tests__/calendar-bucket.test.ts @@ -22,8 +22,8 @@ import { MAX_OCCURRENCES_PER_TASK, } from "../helpers/calendar-bucket"; -const TODAY = new Date(2026, 4, 1); // 2026-05-01 local -const TODAY_ISO = "2026-05-01"; +const TODAY = "2026-05-01"; // HA's today, as haToday() gives it +const TODAY_ISO = TODAY; function addDays(iso: string, days: number): string { const [y, m, d] = iso.split("-").map(Number); @@ -341,10 +341,8 @@ describe("buildCalendarBuckets", () => { // ── v2.2.0 — past-window bucketer ──────────────────────────────────────── function histEntry(daysAgo: number, type = "completed", over: Partial = {}) { - const d = new Date(TODAY); - d.setDate(d.getDate() - daysAgo); return { - timestamp: `${isoDateLocal(d)}T08:30:00`, + timestamp: `${addDays(TODAY_ISO, -daysAgo)}T08:30:00`, type, ...over, }; diff --git a/custom_components/maintenance_supporter/frontend-src/__tests__/warranty.test.ts b/custom_components/maintenance_supporter/frontend-src/__tests__/warranty.test.ts index ca70ec16..99a986a3 100644 --- a/custom_components/maintenance_supporter/frontend-src/__tests__/warranty.test.ts +++ b/custom_components/maintenance_supporter/frontend-src/__tests__/warranty.test.ts @@ -7,7 +7,7 @@ import { expect } from "@open-wc/testing"; import { warrantyStatus, WARRANTY_WARN_DAYS } from "../helpers/warranty"; -const TODAY = new Date(2026, 5, 1); // 2026-06-01 local +const TODAY = "2026-06-01"; // HA's today, as haToday() gives it describe("warrantyStatus (#67)", () => { it("null / undefined / empty / invalid → none", () => { diff --git a/custom_components/maintenance_supporter/frontend-src/components/battery-fleet-section.ts b/custom_components/maintenance_supporter/frontend-src/components/battery-fleet-section.ts index 145e90c6..d10937b0 100644 --- a/custom_components/maintenance_supporter/frontend-src/components/battery-fleet-section.ts +++ b/custom_components/maintenance_supporter/frontend-src/components/battery-fleet-section.ts @@ -11,7 +11,7 @@ import { LS_KEYS, lsGet, lsSet } from "../helpers/storage-keys"; import { runWs } from "../helpers/ws-run"; import { canWrite, NO_DELEGATION, type WriteAccess } from "../helpers/permissions"; import { fetchSettingsOnce } from "../helpers/settings-cache"; -import { isoDateLocal } from "../helpers/calendar-bucket"; +import { addDaysIso, haToday } from "../helpers/ha-time"; import { px } from "../renderers/chart-utils"; import type { HomeAssistant } from "../types"; @@ -332,16 +332,18 @@ export class MaintenanceBatteryFleetSection extends LitElement { * `days_until` comes from last-replaced + typical lifetime, so it is an * estimate — the tilde in the template says so. Negative values (past the * typical lifetime but not reported low yet) render as past dates, which - * is honest: the battery is living on borrowed time. */ + * is honest: the battery is living on borrowed time. The server counts + * the days from HA's today — so does the date (the browser's day plus + * N × 24 h was a day off on a device in another zone). */ private _predictedDate(daysUntil: number): string { - return this._fmtDate(Date.now() + daysUntil * 864e5); + return formatDate(addDaysIso(haToday(), daysUntil), this._lang); } - /** Local calendar date of an epoch, in the HA profile date format (#163 — - * a direct Intl.DateTimeFormat call here ignored the profile and showed - * 9/2/2026 to a DD/MM/YYYY user). */ + /** The calendar date of an epoch (a detected swap), in the HA profile + * date format and time zone (#163 — a direct Intl.DateTimeFormat call + * here ignored the profile and showed 9/2/2026 to a DD/MM/YYYY user). */ private _fmtDate(epochMs: number): string { - return formatDate(isoDateLocal(new Date(epochMs)), this._lang); + return Number.isFinite(epochMs) ? formatDate(new Date(epochMs).toISOString(), this._lang) : "—"; } /** The grouped shopping quantities as CLICKABLE chips: a type filters the diff --git a/custom_components/maintenance_supporter/frontend-src/components/complete-dialog.ts b/custom_components/maintenance_supporter/frontend-src/components/complete-dialog.ts index 7f4fddf5..3ac9229c 100644 --- a/custom_components/maintenance_supporter/frontend-src/components/complete-dialog.ts +++ b/custom_components/maintenance_supporter/frontend-src/components/complete-dialog.ts @@ -4,9 +4,9 @@ import { LitElement, html, css, nothing } from "lit"; import { property, state } from "lit/decorators.js"; import type { HomeAssistant, ReadingSlot, TaskPartLink } from "../types"; import { lastReadingBefore, type ReadingHistoryEntry } from "../helpers/reading-slots"; -import { t, nativeFieldStyles, formatCost, formatNumber, formatQty } from "../styles"; +import { t, nativeFieldStyles, formatCost, formatNumber, formatQty, haTimeZoneHint } from "../styles"; import { runWs } from "../helpers/ws-run"; -import { isoMinuteLocal } from "../helpers/calendar-bucket"; +import { haNowMinute, stampMs } from "../helpers/ha-time"; import { PART_QTY_RANGE, RESTOCK_QTY_RANGE } from "../helpers/setting-ranges"; import { partLinkKey, type LinkedPart } from "../helpers/shared-parts"; import { unitPrice, type PartsCostMode } from "../helpers/parts-cost"; @@ -23,6 +23,15 @@ import { photoPickerStyles } from "./ms-photo-picker"; * One TS source (helpers/setting-ranges), pinned to Python. */ const USED_QTY_RANGE = PART_QTY_RANGE; +/** `task-completed`'s detail: which task, and whether the server remembered + * the completion so the opener may offer Undo. */ +export interface TaskCompletedDetail { + entryId: string; + taskId: string; + taskName: string; + undo: boolean; +} + export class MaintenanceCompleteDialog extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @property() public entryId = ""; @@ -97,7 +106,8 @@ export class MaintenanceCompleteDialog extends LitElement { /** #161 phase 2: typed text per slot id (parsed on save; "" = unread). */ @state() private _readingValues: Record = {}; @state() private _restockQty = ""; - /** #133: optional backdated completion moment ("YYYY-MM-DDTHH:MM:SS" local; "" = now). */ + /** #133: optional backdated completion moment ("YYYY-MM-DDTHH:MM:SS" in + * HA local time — zone-less, the way the backend reads it; "" = now). */ @state() private _completedAt = ""; /** Keyed by `partLinkKey` — the (entry_id, part_id) pair — because two * objects can carry the same part id, so part_id alone would merge pools. */ @@ -203,8 +213,10 @@ export class MaintenanceCompleteDialog extends LitElement { } if (this._completedAt) { // Client-side guard mirrors the backend rule — a picked future moment - // fails fast with a localized message instead of a WS roundtrip. - if (new Date(this._completedAt).getTime() > Date.now()) { + // fails fast with a localized message instead of a WS roundtrip. The + // value is HA local time, so it is judged on HA's clock: read in the + // browser's zone, a moment just past was "future" east of HA. + if (stampMs(this._completedAt) > Date.now()) { this._error = t("completed_at_future_error", this.lang); return; } @@ -270,14 +282,22 @@ export class MaintenanceCompleteDialog extends LitElement { if (res === undefined) return; this._photos.markAttached(); // attached now — Cancel cleanup must not touch them this._open = false; - this.dispatchEvent(new CustomEvent("task-completed")); + // The opener offers Undo when the server remembered the completion for + // this person (task/undo, ten minutes, one level). + const detail: TaskCompletedDetail = { + entryId: this.entryId, + taskId: this.taskId, + taskName: this.taskName, + undo: (res as { undo?: boolean } | null)?.undo === true, + }; + this.dispatchEvent(new CustomEvent("task-completed", { detail })); } /** #161 phase 2: one field per slot with the previous value as a hint; * a value below the last one gets a warning (meters count up — a * replaced meter or a typo), never a block. */ private _renderReadingField(slot: ReadingSlot, L: string) { - const at = this._completedAt ? new Date(this._completedAt).getTime() : NaN; + const at = this._completedAt ? stampMs(this._completedAt) : NaN; const last = lastReadingBefore(this.readingHistory, slot.id, isNaN(at) ? undefined : at); const unit = slot.unit || this.readingUnit; const raw = (this._readingValues[slot.id] ?? "").trim(); @@ -386,9 +406,12 @@ export class MaintenanceCompleteDialog extends LitElement { this._photos.discardOrphans(); } - /** Seed the backdate field with the current minute (local, seconds zeroed). */ + /** Seed the backdate field with HA's current minute (seconds zeroed) — + * the field means HA local time. The browser's minute was refused as + * "future" east of HA and stored hours early west of it (next_due a day + * early once that crossed midnight). */ private _pickCompletedAt(): void { - this._completedAt = isoMinuteLocal(new Date()); + this._completedAt = `${haNowMinute()}:00`; } render() { @@ -525,6 +548,7 @@ export class MaintenanceCompleteDialog extends LitElement { .hass=${this.hass} .lang=${L} .value=${this._completedAt} + .helper=${haTimeZoneHint(L)} @value-changed=${(e: CustomEvent) => (this._completedAt = e.detail.value as string)} >` : html` + `)} + ${rest.length + ? html`+${rest.length}` + : nothing} + `; + } + /** Open a chip: a web link directly, a stored file through a signed path * (the same route the panel uses, so it works in the Companion app). */ private async _openDoc(doc: CardDoc): Promise { @@ -412,8 +445,13 @@ export class MaintenanceSupporterCard extends LitElement { return tasks; } - private _onCompleted = async (): Promise => { + /** After a completion: reload, and — when the server remembered it for + * this person — Home Assistant's own toast offers Undo (the card has no + * toast of its own). */ + private _onCompleted = async (e: CustomEvent): Promise => { await this._loadData(); + const d = e.detail; + if (d?.undo) offerUndoToast(this, this.hass, d, this._lang, () => void this._loadData()); }; /** The row's Complete action — the SAME derivation the panel uses (phase @@ -557,21 +595,7 @@ export class MaintenanceSupporterCard extends LitElement { ` : nothing} - ${this._docsFor(entry_id, task).length - ? html`
- ${this._docsFor(entry_id, task).map((doc) => html` - - `)} -
` - : nothing} + ${this._renderDocChips(entry_id, task)}
${task.days_until_due !== null && task.days_until_due !== undefined ? task.days_until_due < 0 @@ -686,7 +710,9 @@ export class MaintenanceSupporterCard extends LitElement { } .empty-link:hover { text-decoration: underline; } .all-caught-up { color: var(--success-color, #4caf50); font-weight: 500; } - .task-list { padding: 0 16px 16px; } + /* The list measures itself: a dashboard column on a wide screen needs + the narrow row layout as much as a phone does. */ + .task-list { padding: 0 16px 16px; container: ms-tasks / inline-size; } .task-item { display: flex; @@ -730,18 +756,24 @@ export class MaintenanceSupporterCard extends LitElement { text-overflow: ellipsis; } + /* One line of chips that shrinks before the name does; a chip's label + ends in an ellipsis (on the span — a flex button clips its text + without one). */ .doc-chips { display: flex; - flex-wrap: wrap; + flex: 0 1 auto; + min-width: 0; + max-width: 40%; gap: 4px; - margin-right: 6px; - max-width: 45%; + overflow: hidden; } .doc-chip { display: inline-flex; align-items: center; + flex: 0 1 auto; + min-width: 0; gap: 3px; - max-width: 14ch; + max-width: 22ch; padding: 1px 6px; border: 1px solid var(--divider-color, #e0e0e0); border-radius: 10px; @@ -750,12 +782,25 @@ export class MaintenanceSupporterCard extends LitElement { font: inherit; font-size: 11px; cursor: pointer; - overflow: hidden; white-space: nowrap; - text-overflow: ellipsis; } + .doc-chip span { min-width: 0; overflow: hidden; text-overflow: ellipsis; } .doc-chip:hover { color: var(--primary-color); border-color: var(--primary-color); } - .doc-chip ha-icon { --mdc-icon-size: 12px; width: 12px; height: 12px; } + .doc-chip ha-icon { --mdc-icon-size: 12px; width: 12px; height: 12px; flex: none; } + .doc-more { flex: none; } + /* Narrow (a phone, a dashboard column): the chips get a line of their + own under the task. Beside it, three of them left the task's name + one letter wide and broke its object and type over three lines. */ + @container ms-tasks (max-width: 600px) { + .task-item { flex-wrap: wrap; row-gap: 2px; } + .doc-chips { + order: 1; + flex: 1 0 100%; + max-width: none; + box-sizing: border-box; + padding-inline-start: 20px; + } + } /* nowrap: the due label is localized via formatDueDays ("5 d overdue", "5 T überfällig") — without it a narrow phone card wraps that onto a second line and the row grows taller. The name column ellipsizes diff --git a/custom_components/maintenance_supporter/frontend-src/maintenance-panel.ts b/custom_components/maintenance_supporter/frontend-src/maintenance-panel.ts index c07ecca1..ea37680f 100644 --- a/custom_components/maintenance_supporter/frontend-src/maintenance-panel.ts +++ b/custom_components/maintenance_supporter/frontend-src/maintenance-panel.ts @@ -88,7 +88,8 @@ import "./components/parts-section"; import "./components/object-history-section"; import "./components/task-documents"; import type { MaintenanceTaskDialog } from "./components/task-dialog"; -import type { MaintenanceCompleteDialog } from "./components/complete-dialog"; +import type { MaintenanceCompleteDialog, TaskCompletedDetail } from "./components/complete-dialog"; +import { undoCompletion } from "./helpers/undo-completion"; import type { MaintenanceQrDialog } from "./components/qr-dialog"; import type { MaintenanceAdoptProblemSensorsDialog } from "./components/adopt-problem-sensors-dialog"; import "./components/battery-fleet-section"; @@ -113,7 +114,8 @@ import type { MaintenanceGroupDialog } from "./components/group-dialog"; import "./components/saved-views-dialog"; import type { MaintenanceSavedViewsDialog } from "./components/saved-views-dialog"; import { type SparklineContext } from "./renderers/sparkline"; -import { buildCalendarBuckets, isoDateLocal, type CalendarEvent } from "./helpers/calendar-bucket"; +import { buildCalendarBuckets, type CalendarEvent } from "./helpers/calendar-bucket"; +import { haToday } from "./helpers/ha-time"; import { renderTriggerProgress, renderMiniSparkline, computeTrend } from "./renderers/progress"; import { type HistoryContext } from "./renderers/history"; import { renderUserBadge, type TaskDetailContext } from "./renderers/task-detail"; @@ -2909,7 +2911,8 @@ export class MaintenanceSupporterPanel extends LitElement { } return; } - this._showToast(t("quick_complete_success", this._lang), "info"); + if ((res as { undo?: boolean } | null)?.undo) this._offerUndo(entryId, taskId, t("quick_complete_success", this._lang)); + else this._showToast(t("quick_complete_success", this._lang), "info"); // Silent success: the list must reflect the completion right away — the // subscription delta may lag or be coalesced. try { await this._loadData(); } catch { /* subscription will sync */ } @@ -3125,6 +3128,24 @@ export class MaintenanceSupporterPanel extends LitElement { try { await this._loadData(); } catch { /* subscription will sync */ } }; + /** A completion from the complete dialog: refresh, and offer Undo when the + * server remembered it for this person (helpers/undo-completion). */ + private _onTaskCompleted = async (e: CustomEvent): Promise => { + await this._onDialogEvent(); + const d = e.detail; + if (d?.undo) this._offerUndo(d.entryId, d.taskId, t("task_completed_named", this._lang).replace("{task}", d.taskName)); + }; + + private _offerUndo(entryId: string, taskId: string, message: string): void { + this._showUndoToast(message, () => void this._undoCompletion(entryId, taskId)); + } + + private async _undoCompletion(entryId: string, taskId: string): Promise { + const res = await undoCompletion(this.hass, entryId, taskId, this._lang); + this._showToast(res.message, res.ok ? "info" : "error"); + if (res.ok) await this._onDialogEvent(); + } + // --- Render --- render() { @@ -3191,7 +3212,7 @@ export class MaintenanceSupporterPanel extends LitElement { > `${c.object_name ?? ""}/${c.task_name ?? c.task_id}×${c.quantity}`).join(" | ")), ].join(",")); } - const ts = isoDateLocal(new Date()); + const ts = haToday(); downloadTextFile(lines.join("\n"), `maintenance_parts_${ts}.csv`, "text/csv;charset=utf-8"); } @@ -4279,7 +4300,7 @@ export class MaintenanceSupporterPanel extends LitElement { private async _exportObjectsCsv(): Promise { const result = await runWs<{ csv: string }>(this, { type: "maintenance_supporter/objects/csv" }, { onError: (m) => this._showToast(m) }); if (!result) return; - const ts = isoDateLocal(new Date()); + const ts = haToday(); downloadTextFile(result.csv, `maintenance_objects_${ts}.csv`, "text/csv;charset=utf-8"); } diff --git a/custom_components/maintenance_supporter/frontend-src/renderers/chart-utils.ts b/custom_components/maintenance_supporter/frontend-src/renderers/chart-utils.ts index e890deb7..42b5f726 100644 --- a/custom_components/maintenance_supporter/frontend-src/renderers/chart-utils.ts +++ b/custom_components/maintenance_supporter/frontend-src/renderers/chart-utils.ts @@ -1,6 +1,7 @@ /** Shared chart helpers: nice axis ticks and consistent number/date formats. */ import { formatDateShort, formatTimeOfDay, formatNumber } from "../styles"; +import { displayTimeZone, ymdIn } from "../helpers/ha-time"; /** SVG / CSS coordinate: one decimal, ALWAYS a dot — never a user-facing * number. The only place outside styles.ts allowed to call toFixed (tripwire @@ -75,9 +76,12 @@ export function fmtDateTime(ts: number, lang: string): string { } /** Whether date ticks need the year: the range crosses a calendar-year - * boundary (a "Jun … Feb" sequence would otherwise read backwards). */ + * boundary (a "Jun … Feb" sequence would otherwise read backwards) — in + * the zone the ticks are shown in. */ export function needsYear(tsMin: number, tsMax: number): boolean { - return new Date(tsMin).getFullYear() !== new Date(tsMax).getFullYear(); + if (!Number.isFinite(tsMin) || !Number.isFinite(tsMax)) return false; + const zone = displayTimeZone(); + return ymdIn(tsMin, zone).slice(0, 4) !== ymdIn(tsMax, zone).slice(0, 4); } /** Evenly-spaced x-axis tick timestamps (first/last inclusive). */ diff --git a/custom_components/maintenance_supporter/frontend-src/renderers/charts.ts b/custom_components/maintenance_supporter/frontend-src/renderers/charts.ts index 6f1f532a..1b283da2 100644 --- a/custom_components/maintenance_supporter/frontend-src/renderers/charts.ts +++ b/custom_components/maintenance_supporter/frontend-src/renderers/charts.ts @@ -11,6 +11,7 @@ import { t, formatCost } from "../styles"; import { niceTicks, fmtNum, fmtDateTick, timeTicks, needsYear, px } from "./chart-utils"; import type { MaintenanceTask } from "../types"; import { entrySpend } from "../helpers/parts-cost"; +import { stampMs } from "../helpers/ha-time"; const COST_CHART_H = 200; const PAD_T = 10; @@ -63,7 +64,7 @@ export function renderCostDurationCard( function renderHistoryChart(task: MaintenanceTask, lang: string, toggle: "cost" | "duration" | "both", currencySymbol: string) { const entries = task.history .filter((h) => h.type === "completed" && (entrySpend(h) != null || h.duration != null)) - .map((h) => ({ ts: new Date(h.timestamp).getTime(), cost: entrySpend(h) ?? 0, duration: h.duration ?? 0 })) + .map((h) => ({ ts: stampMs(h.timestamp), cost: entrySpend(h) ?? 0, duration: h.duration ?? 0 })) .sort((a, b) => a.ts - b.ts); if (entries.length < 2) return nothing; diff --git a/custom_components/maintenance_supporter/frontend-src/renderers/history.ts b/custom_components/maintenance_supporter/frontend-src/renderers/history.ts index 4d25b4b1..4c1172bd 100644 --- a/custom_components/maintenance_supporter/frontend-src/renderers/history.ts +++ b/custom_components/maintenance_supporter/frontend-src/renderers/history.ts @@ -20,6 +20,7 @@ import { renderRefChip } from "../helpers/reference"; import { entryReadingValues } from "../helpers/reading-slots"; import { matchesQuery } from "../helpers/search-match"; import { historyNoteText } from "../helpers/history-note"; +import { stampMs } from "../helpers/ha-time"; /** v2.2.0: only "lifecycle + cost-bearing" entries are user-editable. * Triggers are auto-generated by sensors and shouldn't be retroactively @@ -34,9 +35,9 @@ export const EDITABLE_HISTORY_TYPES: readonly HistoryEntryType[] = ["completed", * an unparsable) timestamp keep "later appended = newer". */ export function newestFirst(history: readonly T[] | null | undefined): T[] { const ms = (e: T): number => { - const raw = e.timestamp ?? ""; - // Date-only strings are local days (like formatDate) — not UTC midnight. - const v = Date.parse(raw.length === 10 ? `${raw}T00:00:00` : raw); + // A zone-less stamp (the history editor writes one) is HA local time, + // a date-only one HA's midnight — never UTC midnight. + const v = stampMs(e.timestamp); return Number.isNaN(v) ? -Infinity : v; }; return (history ?? []) diff --git a/custom_components/maintenance_supporter/frontend-src/renderers/progress.ts b/custom_components/maintenance_supporter/frontend-src/renderers/progress.ts index 0c4f507b..a9415ba1 100644 --- a/custom_components/maintenance_supporter/frontend-src/renderers/progress.ts +++ b/custom_components/maintenance_supporter/frontend-src/renderers/progress.ts @@ -15,6 +15,7 @@ import { html, nothing } from "lit"; import { t, formatDate, formatDueDays, formatNumber } from "../styles"; import { px } from "./chart-utils"; import { daysProgress } from "../helpers/interval"; +import { stampMs } from "../helpers/ha-time"; import type { MaintenanceTask, TaskRow, StatisticsPoint } from "../types"; const MINI_SPARKLINE_W = 60; @@ -61,7 +62,7 @@ export function computeTrend( ? [] : (row.history || []) .filter((h) => h.trigger_value != null) - .map((h) => ({ ts: new Date(h.timestamp).getTime(), val: h.trigger_value as number })); + .map((h) => ({ ts: stampMs(h.timestamp), val: h.trigger_value as number })); if (row.trigger_current_value != null) points = [...points, { ts: Date.now(), val: row.trigger_current_value }]; if (points.length < 2) return null; points.sort((a, b) => a.ts - b.ts); @@ -249,7 +250,7 @@ export function renderMiniSparkline( if (!row.history) return nothing; for (const h of row.history) { if (h.trigger_value != null) { - points.push({ ts: new Date(h.timestamp).getTime(), val: h.trigger_value }); + points.push({ ts: stampMs(h.timestamp), val: h.trigger_value }); } } } diff --git a/custom_components/maintenance_supporter/frontend-src/renderers/seasonal.ts b/custom_components/maintenance_supporter/frontend-src/renderers/seasonal.ts index bc52155f..7b960af7 100644 --- a/custom_components/maintenance_supporter/frontend-src/renderers/seasonal.ts +++ b/custom_components/maintenance_supporter/frontend-src/renderers/seasonal.ts @@ -3,6 +3,7 @@ import { html, svg, nothing } from "lit"; import { t, formatNumber } from "../styles"; import { px } from "./chart-utils"; +import { haToday } from "../helpers/ha-time"; import type { MaintenanceTask, AdvancedFeatures } from "../types"; const MONTH_KEYS = [ @@ -11,13 +12,18 @@ const MONTH_KEYS = [ "month_sep", "month_oct", "month_nov", "month_dec", ]; +/** HA's current month (0 = Jan) — the one the backend applies the factor of. */ +function haMonth(): number { + return Number(haToday().slice(5, 7)) - 1; +} + export function renderSeasonalCardCompact(task: MaintenanceTask, lang: string, features: AdvancedFeatures) { if (!features.seasonal || !task.seasonal_factor || task.seasonal_factor === 1.0) { return nothing; } const months = MONTH_KEYS.map(k => t(k, lang)); - const currentMonth = new Date().getMonth(); + const currentMonth = haMonth(); const realFactors = task.seasonal_factors || task.interval_analysis?.seasonal_factors || null; const seasonalData = realFactors && realFactors.length === 12 @@ -66,7 +72,7 @@ function renderSeasonalChart(task: MaintenanceTask, lang: string) { if (!factors || factors.length !== 12) return nothing; const reason = task.interval_analysis?.seasonal_reason; - const currentMonth = new Date().getMonth(); + const currentMonth = haMonth(); const W = 300, H = 100; const PAD_T = 8, PAD_B = 4; const chartH = H - PAD_T - PAD_B; diff --git a/custom_components/maintenance_supporter/frontend-src/renderers/sparkline.ts b/custom_components/maintenance_supporter/frontend-src/renderers/sparkline.ts index c90020e6..d72f5acb 100644 --- a/custom_components/maintenance_supporter/frontend-src/renderers/sparkline.ts +++ b/custom_components/maintenance_supporter/frontend-src/renderers/sparkline.ts @@ -15,6 +15,7 @@ import { html, nothing } from "lit"; import { t, fireMoreInfo, formatDueDays } from "../styles"; import { fmtNum, fmtVal } from "./chart-utils"; import { dueDaysOf } from "./progress"; +import { stampMs } from "../helpers/ha-time"; import "../components/trigger-chart"; import type { ChartPoint, ChartEvent } from "../components/trigger-chart"; import type { MaintenanceTask, TriggerConfig, StatisticsPoint } from "../types"; @@ -299,8 +300,8 @@ function counterBaseline(task: MaintenanceTask, rawPoints: ChartPoint[]): { valu function lastServiceTs(task: MaintenanceTask): number | null { const e = [...task.history] .filter((h) => h.type === "completed" || h.type === "reset") - .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())[0]; - return e ? new Date(e.timestamp).getTime() : null; + .sort((a, b) => stampMs(b.timestamp) - stampMs(a.timestamp))[0]; + return e ? stampMs(e.timestamp) : null; } /** "8,507 / 15,000 km · 57 %" header + progress bar (counter / state_change / runtime). */ @@ -351,7 +352,7 @@ function rawStatsPoints(task: MaintenanceTask, ctx: SparklineContext): ChartPoin } else { for (const h of task.history) { if (h.trigger_value != null) { - points.push({ ts: new Date(h.timestamp).getTime(), val: h.trigger_value }); + points.push({ ts: stampMs(h.timestamp), val: h.trigger_value }); } } } @@ -472,7 +473,7 @@ function renderChart(task: MaintenanceTask, unit: string, ctx: SparklineContext) const events: ChartEvent[] = task.history .filter((h) => ["completed", "skipped", "reset"].includes(h.type)) - .map((h) => ({ ts: new Date(h.timestamp).getTime(), type: h.type })); + .map((h) => ({ ts: stampMs(h.timestamp), type: h.type })); return html` void; }, changedProps: Map, ): void { if (changedProps.has("hass")) { - setProfilePrefs(host.hass?.locale as Parameters[0], host.hass?.config?.country); + setProfilePrefs( + host.hass?.locale as Parameters[0], + host.hass?.config?.country, + host.hass?.config?.time_zone, + ); } const lang = host.hass?.language; if (lang && !isLocaleLoaded(lang)) { @@ -101,6 +107,11 @@ interface ProfilePrefs { time?: string; number?: string; country?: string; + /** The profile "Time zone" option ("local" | "server") and + * hass.config.time_zone — read by helpers/ha-time (HA's clock for the + * schedule, the display zone for timestamps). */ + timeZone?: string; + serverTimeZone?: string; } const _w = window as unknown as { __msDateTimePrefs?: ProfilePrefs }; const DT_PREFS: ProfilePrefs = (_w.__msDateTimePrefs ??= {}); @@ -124,18 +135,24 @@ export function syncCurrencyDecimals(src: { currency_decimals?: number | null } if (src && src.currency_decimals !== undefined && src.currency_decimals !== null) setCurrencyDecimals(Number(src.currency_decimals)); } -/** Feed HA's per-user date/time/number formats into the formatters below. - * `country` is the SERVER's configured country (#140): with the profile - * date format left on "language" it regionalizes the language default — - * "en" + AU renders DD/MM/YYYY instead of the hard en-US mapping. */ +/** Feed HA's per-user date/time/number formats and time zone option into + * the formatters below. `country` is the SERVER's configured country + * (#140): with the profile date format left on "language" it regionalizes + * the language default — "en" + AU renders DD/MM/YYYY instead of the hard + * en-US mapping. `serverTimeZone` is hass.config.time_zone, the clock every + * schedule date runs on (helpers/ha-time); it is kept even without a locale + * (a test mock, an old core). For both, undefined keeps the stored value. */ export function setProfilePrefs( - locale?: { date_format?: string; time_format?: string; number_format?: string }, + locale?: { date_format?: string; time_format?: string; number_format?: string; time_zone?: string }, country?: string | null, + serverTimeZone?: string | null, ): void { + if (serverTimeZone !== undefined) DT_PREFS.serverTimeZone = serverTimeZone || undefined; if (!locale) return; DT_PREFS.date = locale.date_format; DT_PREFS.time = locale.time_format; DT_PREFS.number = locale.number_format; + DT_PREFS.timeZone = locale.time_zone; if (country !== undefined) DT_PREFS.country = country || undefined; } @@ -217,56 +234,79 @@ function resolveLocale(lang?: string): string { return base; } -function _formatDateObj(d: Date, lang?: string): string { - const dd = String(d.getDate()).padStart(2, "0"); - const mm = String(d.getMonth() + 1).padStart(2, "0"); - const yyyy = String(d.getFullYear()); - switch (DT_PREFS.date) { - case "DMY": return `${dd}/${mm}/${yyyy}`; - case "MDY": return `${mm}/${dd}/${yyyy}`; - case "YMD": return `${yyyy}-${mm}-${dd}`; - case "system": - return d.toLocaleDateString(undefined, { day: "2-digit", month: "2-digit", year: "numeric" }); - default: // "language" or unset — UI language, regionalized by the server country (#140) - return d.toLocaleDateString(resolveLocale(lang), { day: "2-digit", month: "2-digit", year: "numeric" }); +/** The profile's numeric date of an instant, read in `timeZone` — the + * display zone for a timestamp, UTC for a date-only value pinned to UTC + * midnight. The explicit orders are built from formatToParts (ha-time + * ymdIn): getDate()/getMonth() read the BROWSER's zone whatever the + * profile says. */ +function _formatDateObj(d: Date, lang: string | undefined, timeZone: string): string { + const order = DT_PREFS.date; + if (order === "DMY" || order === "MDY" || order === "YMD") { + const [yyyy, mm, dd] = ymdIn(d, timeZone).split("-"); + return order === "DMY" ? `${dd}/${mm}/${yyyy}` : order === "MDY" ? `${mm}/${dd}/${yyyy}` : `${yyyy}-${mm}-${dd}`; } + // "system" = the browser's locale; "language" or unset = the UI language, + // regionalized by the server country (#140). + const locale = order === "system" ? undefined : resolveLocale(lang); + return d.toLocaleDateString(locale, { day: "2-digit", month: "2-digit", year: "numeric", timeZone }); } -/** Time of day honouring the HA profile time format (12/24h). Every - * clock-time string in the UI goes through here — chart crosshairs included - * (#163: a bare toLocaleDateString({hour, minute}) ignored the profile). */ +/** Time of day of an instant honouring the HA profile time format (12/24h) + * and its time zone. Every clock-time string in the UI goes through here — + * chart crosshairs included (#163: a bare toLocaleDateString({hour, + * minute}) ignored the profile). */ export function formatTimeOfDay(d: Date, lang?: string): string { + const timeZone = displayTimeZone(); switch (DT_PREFS.time) { - case "12": return d.toLocaleTimeString(resolveLocale(lang), { hour: "2-digit", minute: "2-digit", hour12: true }); - case "24": return d.toLocaleTimeString(resolveLocale(lang), { hour: "2-digit", minute: "2-digit", hour12: false }); - case "system": return d.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit" }); - default: return d.toLocaleTimeString(resolveLocale(lang), { hour: "2-digit", minute: "2-digit" }); + case "12": return d.toLocaleTimeString(resolveLocale(lang), { hour: "2-digit", minute: "2-digit", hour12: true, timeZone }); + case "24": return d.toLocaleTimeString(resolveLocale(lang), { hour: "2-digit", minute: "2-digit", hour12: false, timeZone }); + case "system": return d.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", timeZone }); + default: return d.toLocaleTimeString(resolveLocale(lang), { hour: "2-digit", minute: "2-digit", timeZone }); } } -/** Format a date string (ISO) honouring the HA profile date format. - * Appends T00:00:00 to date-only strings so JS parses them as local time, not UTC. */ +const DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/; + +/** Format a date string (ISO) honouring the HA profile date format. A + * date-only value ("YYYY-MM-DD": next_due, warranty, …) has no zone and + * keeps its day whatever the zones say; a timestamp shows its day in the + * profile's time zone (helpers/ha-time — a zone-less one is HA local time). */ export function formatDate(iso: string | null | undefined, lang?: string): string { if (!iso) return "—"; try { - const local = iso.includes("T") ? iso : iso + "T00:00:00"; - return _formatDateObj(new Date(local), lang); + const day = DATE_ONLY.exec(iso); + if (day) return _formatDateObj(new Date(Date.UTC(+day[1], +day[2] - 1, +day[3])), lang, "UTC"); + return _formatDateObj(new Date(stampMs(iso)), lang, displayTimeZone()); } catch { return iso; } } -/** Format a datetime string (ISO) honouring the HA profile date/time formats. */ +/** Format a datetime string (ISO) honouring the HA profile date/time formats + * and time zone (a zone-less value is HA local time; a date-only one has no + * time of day to show). */ export function formatDateTime(iso: string | null | undefined, lang?: string): string { if (!iso) return "—"; + if (DATE_ONLY.test(iso)) return formatDate(iso, lang); try { - const d = new Date(iso); - return _formatDateObj(d, lang) + " " + formatTimeOfDay(d, lang); + const d = new Date(stampMs(iso)); + return _formatDateObj(d, lang, displayTimeZone()) + " " + formatTimeOfDay(d, lang); } catch { return iso; } } +/** The hint under a field whose value is HA local time (a back-dated + * completion, a history entry's timestamp): it names HA's zone when the + * times shown everywhere else run on another clock — a device elsewhere + * with the profile's "local" zone — and is "" otherwise. Compared by the + * clock, not by name: "Europe/Berlin" and "Europe/Zurich" agree. */ +export function haTimeZoneHint(lang?: string, at: number = Date.now()): string { + const zone = haTimeZone(); + if (minuteIn(at, zone) === minuteIn(at, displayTimeZone())) return ""; + return t("ha_time_zone_hint", lang).replace("{zone}", zone); +} + /** Format "days until due" in localized manner. */ export function formatDueDays(days: number | null | undefined, lang?: string): string { if (days === null || days === undefined) return "—"; @@ -320,13 +360,12 @@ export function formatMonth(d: Date, lang?: string, style: "long" | "short" = "l return monthName(d.getMonth(), lang, style); } -/** Short day+month ("Jul 3" / "3. Juli"), with a 2-digit year when the - * caller's range needs disambiguating — chart axis ticks and tooltips. */ +/** Short day+month of an instant ("Jul 3" / "3. Juli") in the profile's + * time zone, with a 2-digit year when the caller's range needs + * disambiguating — chart axis ticks and tooltips. */ export function formatDateShort(d: Date, lang?: string, withYear = false): string { - return d.toLocaleDateString( - resolveLocale(lang), - withYear ? { month: "short", day: "numeric", year: "2-digit" } : { month: "short", day: "numeric" }, - ); + const shape: Intl.DateTimeFormatOptions = withYear ? { month: "short", day: "numeric", year: "2-digit" } : { month: "short", day: "numeric" }; + return d.toLocaleDateString(resolveLocale(lang), { ...shape, timeZone: displayTimeZone() }); } /** Recurrence shape carried on the WS payload (see types.TaskSchedule). */ diff --git a/custom_components/maintenance_supporter/frontend-src/types.ts b/custom_components/maintenance_supporter/frontend-src/types.ts index c6ec0fee..3fa73955 100644 --- a/custom_components/maintenance_supporter/frontend-src/types.ts +++ b/custom_components/maintenance_supporter/frontend-src/types.ts @@ -673,10 +673,13 @@ export interface HomeAssistant { // date_format/time_format are HA's per-user PROFILE settings ("language" | // "system" | "DMY" | "MDY" | "YMD" resp. "language" | "system" | "12" | "24") // — issue #97: dates must follow them, not just the UI language. - locale?: { language: string; number_format?: string; date_format?: string; time_format?: string }; + // time_zone is the profile's "Time zone": "local" (the browser's, HA's + // default) or "server" — the zone timestamps are SHOWN in. + locale?: { language: string; number_format?: string; date_format?: string; time_format?: string; time_zone?: string }; /** Server config — country (#140) feeds the "language"-default date - * format ("en" + AU → en-AU → DD/MM/YYYY). */ - config?: { country?: string | null }; + * format ("en" + AU → en-AU → DD/MM/YYYY); time_zone is HA's own zone, + * the one next_due / days_until_due count in (helpers/ha-time). */ + config?: { country?: string | null; time_zone?: string | null }; localize(key: string, ...args: unknown[]): string; user?: { id: string; name: string; is_admin: boolean; is_owner: boolean }; /** HA's registered panels; a custom panel carries its module URL under diff --git a/custom_components/maintenance_supporter/frontend-src/ws-errors.ts b/custom_components/maintenance_supporter/frontend-src/ws-errors.ts index 27cd7421..61e4887e 100644 --- a/custom_components/maintenance_supporter/frontend-src/ws-errors.ts +++ b/custom_components/maintenance_supporter/frontend-src/ws-errors.ts @@ -25,6 +25,9 @@ export const WS_ERROR_CODE_KEYS: Record = { already_archived: "ws_err_already_archived", already_paused: "ws_err_already_paused", archived: "ws_err_archived", + // task/undo (2026-10): the person's last completion is another task / gone, + // or the task changed since. + changed_since: "undo_changed", create_failed: "ws_err_create_failed", duplicate_failed: "ws_err_duplicate_failed", empty: "ws_err_empty", @@ -55,6 +58,7 @@ export const WS_ERROR_CODE_KEYS: Record = { not_found: "ws_err_not_found", not_loaded: "ws_err_not_loaded", not_paused: "ws_err_not_paused", + nothing_to_undo: "undo_nothing", replace_failed: "ws_err_replace_failed", self_link_device: "ws_err_self_link_device", storage_unavailable: "ws_err_storage_unavailable", diff --git a/custom_components/maintenance_supporter/frontend/locales/cs.json b/custom_components/maintenance_supporter/frontend/locales/cs.json index 1b009e1b..496d5a89 100644 --- a/custom_components/maintenance_supporter/frontend/locales/cs.json +++ b/custom_components/maintenance_supporter/frontend/locales/cs.json @@ -210,6 +210,7 @@ "completed_at_optional": "Dokončeno dne (volitelné, prázdné = nyní)", "completed_at_pick": "Nastavit datum a čas", "completed_at_future_error": "Datum dokončení nesmí být v budoucnosti.", + "ha_time_zone_hint": "Čas v časovém pásmu Home Assistantu ({zone})", "days": "dní", "day": "den", "today": "Dnes", @@ -554,7 +555,8 @@ "cal_forward_windows": "Budoucí okna", "history_edit_title": "Upravit záznam historie", "history_delete_entry": "Smazat záznam", - "history_delete_confirm": "Smazat tento záznam historie? Datum posledního provedení se vrátí k předchozímu dokončení; fotografie zůstanou u objektu a spotřebované díly se nevrátí na sklad.", + "history_delete_confirm_entry": "Smazat tento záznam historie? Datum posledního provedení se vrátí na předchozí; fotky zůstanou u objektu.", + "history_delete_returns_parts": "Použité díly se vrátí do zásoby: {parts}.", "history_edit_timestamp": "Časové razítko", "manufacturer": "Výrobce", "model": "Model", @@ -848,6 +850,11 @@ "loading": "Načítání…", "archive": "Archivovat", "undo": "Zpět", + "task_completed_named": "Dokončeno: {task}", + "undo_done": "Dokončení vráceno zpět", + "undo_done_action_ran": "Dokončení vráceno zpět. Akce při dokončení už proběhla a nelze ji vrátit.", + "undo_nothing": "Není co vrátit. Vrátit jde jen vaše poslední dokončení, do 10 minut.", + "undo_changed": "Nelze vrátit. Úkol se mezitím změnil.", "task_archived": "Úkol archivován", "object_archived": "Objekt archivován", "unarchive": "Obnovit z archivu", diff --git a/custom_components/maintenance_supporter/frontend/locales/da.json b/custom_components/maintenance_supporter/frontend/locales/da.json index 6af60d1d..ee9fe532 100644 --- a/custom_components/maintenance_supporter/frontend/locales/da.json +++ b/custom_components/maintenance_supporter/frontend/locales/da.json @@ -212,6 +212,7 @@ "completed_at_optional": "Udført den (valgfrit, tomt = nu)", "completed_at_pick": "Vælg dato og tidspunkt", "completed_at_future_error": "Udførelsesdatoen må ikke ligge i fremtiden.", + "ha_time_zone_hint": "Tid i Home Assistants tidszone ({zone})", "days": "dage", "day": "dag", "today": "I dag", @@ -556,7 +557,8 @@ "cal_forward_windows": "Kommende vinduer", "history_edit_title": "Rediger historikpost", "history_delete_entry": "Slet post", - "history_delete_confirm": "Slet denne historikpost? Datoen for seneste udførelse falder tilbage til den forrige; fotos bliver hos objektet, og forbrugte dele lægges ikke tilbage på lager.", + "history_delete_confirm_entry": "Slet denne historikpost? Datoen for sidste udførelse falder tilbage til den forrige; billeder bliver ved objektet.", + "history_delete_returns_parts": "De brugte dele går tilbage på lager: {parts}.", "history_edit_timestamp": "Tidsstempel", "manufacturer": "Producent", "model": "Model", @@ -848,6 +850,11 @@ "loading": "Indlæser…", "archive": "Arkivér", "undo": "Fortryd", + "task_completed_named": "Fuldført: {task}", + "undo_done": "Udførelsen fortrudt", + "undo_done_action_ran": "Udførelsen fortrudt. Handlingen ved udførelse var allerede kørt og kan ikke fortrydes.", + "undo_nothing": "Intet at fortryde. Kun din egen seneste udførelse kan fortrydes, inden for 10 minutter.", + "undo_changed": "Kan ikke fortrydes. Opgaven er ændret siden.", "task_archived": "Opgave arkiveret", "object_archived": "Objekt arkiveret", "unarchive": "Gendan", diff --git a/custom_components/maintenance_supporter/frontend/locales/de.json b/custom_components/maintenance_supporter/frontend/locales/de.json index ab6af5cf..d6be2e3b 100644 --- a/custom_components/maintenance_supporter/frontend/locales/de.json +++ b/custom_components/maintenance_supporter/frontend/locales/de.json @@ -212,6 +212,7 @@ "completed_at_optional": "Erledigt am (optional, leer = jetzt)", "completed_at_pick": "Datum & Uhrzeit wählen", "completed_at_future_error": "Das Erledigungsdatum darf nicht in der Zukunft liegen.", + "ha_time_zone_hint": "Uhrzeit in der Zeitzone von Home Assistant ({zone})", "days": "Tage", "day": "Tag", "today": "Heute", @@ -556,7 +557,8 @@ "cal_forward_windows": "Zukünftige Fenster", "history_edit_title": "Verlaufseintrag bearbeiten", "history_delete_entry": "Eintrag löschen", - "history_delete_confirm": "Diesen Verlaufseintrag löschen? Das Datum der letzten Erledigung fällt auf die vorherige zurück; Fotos bleiben beim Objekt, verbrauchte Teile werden nicht zurückgebucht.", + "history_delete_confirm_entry": "Diesen Verlaufseintrag löschen? Das Datum der letzten Erledigung fällt auf die vorherige zurück; Fotos bleiben beim Objekt.", + "history_delete_returns_parts": "Die dabei verbrauchten Teile gehen zurück in den Bestand: {parts}.", "history_edit_timestamp": "Zeitstempel", "manufacturer": "Hersteller", "model": "Modell", @@ -848,6 +850,11 @@ "loading": "Laden…", "archive": "Archivieren", "undo": "Rückgängig", + "task_completed_named": "Erledigt: {task}", + "undo_done": "Erledigung zurückgenommen", + "undo_done_action_ran": "Erledigung zurückgenommen. Die Aktion beim Erledigen war schon gelaufen und lässt sich nicht zurücknehmen.", + "undo_nothing": "Nichts zurückzunehmen. Nur deine eigene letzte Erledigung lässt sich zurücknehmen, innerhalb von 10 Minuten.", + "undo_changed": "Zurücknehmen nicht möglich. Die Aufgabe wurde inzwischen geändert.", "task_archived": "Aufgabe archiviert", "object_archived": "Objekt archiviert", "unarchive": "Wiederherstellen", diff --git a/custom_components/maintenance_supporter/frontend/locales/en.json b/custom_components/maintenance_supporter/frontend/locales/en.json index 57e0cd24..963791e3 100644 --- a/custom_components/maintenance_supporter/frontend/locales/en.json +++ b/custom_components/maintenance_supporter/frontend/locales/en.json @@ -212,6 +212,7 @@ "completed_at_optional": "Completed at (optional, empty = now)", "completed_at_pick": "Set date & time", "completed_at_future_error": "The completion date cannot be in the future.", + "ha_time_zone_hint": "Time in Home Assistant's time zone ({zone})", "days": "days", "day": "day", "today": "Today", @@ -556,7 +557,8 @@ "cal_forward_windows": "Forward windows", "history_edit_title": "Edit history entry", "history_delete_entry": "Delete entry", - "history_delete_confirm": "Delete this history entry? The task's last-performed date falls back to the previous completion; photos stay with the object and consumed parts are not restocked.", + "history_delete_confirm_entry": "Delete this history entry? The task's last-performed date falls back to the previous completion; photos stay with the object.", + "history_delete_returns_parts": "The parts it used go back to stock: {parts}.", "history_edit_timestamp": "Timestamp", "manufacturer": "Manufacturer", "model": "Model", @@ -855,6 +857,11 @@ "loading": "Loading…", "archive": "Archive", "undo": "Undo", + "task_completed_named": "Completed: {task}", + "undo_done": "Completion undone", + "undo_done_action_ran": "Completion undone. Its completion action had already run and cannot be taken back.", + "undo_nothing": "Nothing to undo. Only your own last completion can be taken back, within 10 minutes.", + "undo_changed": "Can't undo. The task has changed since.", "task_archived": "Task archived", "object_archived": "Object archived", "unarchive": "Unarchive", diff --git a/custom_components/maintenance_supporter/frontend/locales/es.json b/custom_components/maintenance_supporter/frontend/locales/es.json index 9e926da6..6a35fc58 100644 --- a/custom_components/maintenance_supporter/frontend/locales/es.json +++ b/custom_components/maintenance_supporter/frontend/locales/es.json @@ -210,6 +210,7 @@ "completed_at_optional": "Completado el (opcional, vacío = ahora)", "completed_at_pick": "Elegir fecha y hora", "completed_at_future_error": "La fecha de finalización no puede estar en el futuro.", + "ha_time_zone_hint": "Hora en la zona horaria de Home Assistant ({zone})", "days": "días", "day": "día", "today": "Hoy", @@ -554,7 +555,8 @@ "cal_forward_windows": "Ventanas futuras", "history_edit_title": "Editar entrada del historial", "history_delete_entry": "Eliminar entrada", - "history_delete_confirm": "¿Eliminar esta entrada del historial? La fecha de última realización vuelve a la anterior; las fotos permanecen con el objeto y las piezas consumidas no se reponen.", + "history_delete_confirm_entry": "¿Eliminar esta entrada del historial? La fecha de la última realización vuelve a la anterior; las fotos se quedan con el objeto.", + "history_delete_returns_parts": "Las piezas usadas vuelven al stock: {parts}.", "history_edit_timestamp": "Marca de tiempo", "manufacturer": "Fabricante", "model": "Modelo", @@ -848,6 +850,11 @@ "loading": "Cargando…", "archive": "Archivar", "undo": "Deshacer", + "task_completed_named": "Completada: {task}", + "undo_done": "Realización deshecha", + "undo_done_action_ran": "Realización deshecha. La acción al completar ya se había ejecutado y no se puede deshacer.", + "undo_nothing": "No hay nada que deshacer. Solo puedes deshacer tu última realización, en un plazo de 10 minutos.", + "undo_changed": "No se puede deshacer. La tarea ha cambiado desde entonces.", "task_archived": "Tarea archivada", "object_archived": "Objeto archivado", "unarchive": "Desarchivar", diff --git a/custom_components/maintenance_supporter/frontend/locales/fi.json b/custom_components/maintenance_supporter/frontend/locales/fi.json index d1a4d7af..9dfb0ca3 100644 --- a/custom_components/maintenance_supporter/frontend/locales/fi.json +++ b/custom_components/maintenance_supporter/frontend/locales/fi.json @@ -212,6 +212,7 @@ "completed_at_optional": "Suoritettu (valinnainen, tyhjä = nyt)", "completed_at_pick": "Aseta päivä ja aika", "completed_at_future_error": "Suorituspäivä ei voi olla tulevaisuudessa.", + "ha_time_zone_hint": "Aika Home Assistantin aikavyöhykkeellä ({zone})", "days": "päivää", "day": "päivä", "today": "Tänään", @@ -556,7 +557,8 @@ "cal_forward_windows": "Tulevat jaksot", "history_edit_title": "Muokkaa historiamerkintää", "history_delete_entry": "Poista merkintä", - "history_delete_confirm": "Poistetaanko tämä historiamerkintä? Viimeisimmän suorituksen päivä palautuu edelliseen; kuvat jäävät kohteelle eikä kulutettuja osia palauteta varastoon.", + "history_delete_confirm_entry": "Poistetaanko tämä historiamerkintä? Viimeisin suorituspäivä palaa edelliseen; kuvat jäävät kohteelle.", + "history_delete_returns_parts": "Käytetyt osat palautuvat varastoon: {parts}.", "history_edit_timestamp": "Aikaleima", "manufacturer": "Valmistaja", "model": "Malli", @@ -848,6 +850,11 @@ "loading": "Ladataan…", "archive": "Arkistoi", "undo": "Kumoa", + "task_completed_named": "Suoritettu: {task}", + "undo_done": "Suoritus peruttu", + "undo_done_action_ran": "Suoritus peruttu. Suorituksen toiminto oli jo ajettu, eikä sitä voi perua.", + "undo_nothing": "Ei mitään peruttavaa. Vain oman viimeisimmän suorituksen voi perua, 10 minuutin sisällä.", + "undo_changed": "Perua ei voi. Tehtävä on muuttunut sen jälkeen.", "task_archived": "Tehtävä arkistoitu", "object_archived": "Kohde arkistoitu", "unarchive": "Palauta arkistosta", diff --git a/custom_components/maintenance_supporter/frontend/locales/fr.json b/custom_components/maintenance_supporter/frontend/locales/fr.json index 2ac2a088..b83b4dfc 100644 --- a/custom_components/maintenance_supporter/frontend/locales/fr.json +++ b/custom_components/maintenance_supporter/frontend/locales/fr.json @@ -210,6 +210,7 @@ "completed_at_optional": "Effectué le (optionnel, vide = maintenant)", "completed_at_pick": "Choisir la date et l'heure", "completed_at_future_error": "La date d'achèvement ne peut pas être dans le futur.", + "ha_time_zone_hint": "Heure dans le fuseau horaire de Home Assistant ({zone})", "days": "jours", "day": "jour", "today": "Aujourd'hui", @@ -554,7 +555,8 @@ "cal_forward_windows": "Fenêtres à venir", "history_edit_title": "Modifier l'entrée d'historique", "history_delete_entry": "Supprimer l'entrée", - "history_delete_confirm": "Supprimer cette entrée d'historique ? La date de dernière réalisation revient à la précédente ; les photos restent avec l'objet et les pièces consommées ne sont pas remises en stock.", + "history_delete_confirm_entry": "Supprimer cette entrée d'historique ? La date de dernière réalisation revient à la précédente ; les photos restent avec l'objet.", + "history_delete_returns_parts": "Les pièces utilisées retournent en stock : {parts}.", "history_edit_timestamp": "Horodatage", "manufacturer": "Fabricant", "model": "Modèle", @@ -848,6 +850,11 @@ "loading": "Chargement…", "archive": "Archiver", "undo": "Annuler", + "task_completed_named": "Terminé : {task}", + "undo_done": "Réalisation annulée", + "undo_done_action_ran": "Réalisation annulée. L'action de fin s'était déjà exécutée et ne peut pas être annulée.", + "undo_nothing": "Rien à annuler. Seule votre dernière réalisation peut être annulée, dans les 10 minutes.", + "undo_changed": "Annulation impossible. La tâche a changé depuis.", "task_archived": "Tâche archivée", "object_archived": "Objet archivé", "unarchive": "Désarchiver", diff --git a/custom_components/maintenance_supporter/frontend/locales/hi.json b/custom_components/maintenance_supporter/frontend/locales/hi.json index 15cf4b35..ff6aad53 100644 --- a/custom_components/maintenance_supporter/frontend/locales/hi.json +++ b/custom_components/maintenance_supporter/frontend/locales/hi.json @@ -212,6 +212,7 @@ "completed_at_optional": "पूर्ण होने का समय (वैकल्पिक, खाली = अभी)", "completed_at_pick": "तारीख और समय चुनें", "completed_at_future_error": "पूर्णता की तारीख भविष्य में नहीं हो सकती।", + "ha_time_zone_hint": "Home Assistant के समय क्षेत्र का समय ({zone})", "days": "दिन", "day": "दिन", "today": "आज", @@ -556,7 +557,8 @@ "cal_forward_windows": "आगामी अवधियाँ", "history_edit_title": "इतिहास प्रविष्टि संपादित करें", "history_delete_entry": "प्रविष्टि हटाएँ", - "history_delete_confirm": "क्या यह इतिहास प्रविष्टि हटाएँ? अंतिम निष्पादन की तिथि पिछली पूर्णता पर लौट जाएगी; फ़ोटो ऑब्जेक्ट के पास रहेंगे और उपयोग किए गए पुर्जे स्टॉक में वापस नहीं जोड़े जाएँगे।", + "history_delete_confirm_entry": "यह इतिहास प्रविष्टि हटाएँ? अंतिम बार करने की तिथि पिछली पूर्णता पर लौट आएगी; फ़ोटो ऑब्जेक्ट के साथ रहेंगी।", + "history_delete_returns_parts": "इस्तेमाल किए गए पुर्ज़े स्टॉक में वापस जाएँगे: {parts}।", "history_edit_timestamp": "टाइमस्टैम्प", "manufacturer": "निर्माता", "model": "मॉडल", @@ -848,6 +850,11 @@ "loading": "लोड हो रहा है…", "archive": "संग्रह करें", "undo": "पूर्ववत करें", + "task_completed_named": "पूर्ण हुआ: {task}", + "undo_done": "पूर्णता वापस ली गई", + "undo_done_action_ran": "पूर्णता वापस ली गई। पूरा करने पर चलने वाली कार्रवाई पहले ही चल चुकी थी, उसे वापस नहीं लिया जा सकता।", + "undo_nothing": "वापस लेने को कुछ नहीं है। केवल आपकी अपनी पिछली पूर्णता 10 मिनट के भीतर वापस ली जा सकती है।", + "undo_changed": "वापस नहीं लिया जा सकता। तब से कार्य बदल चुका है।", "task_archived": "कार्य संग्रहित", "object_archived": "ऑब्जेक्ट संग्रहित", "unarchive": "संग्रह से हटाएँ", diff --git a/custom_components/maintenance_supporter/frontend/locales/hu.json b/custom_components/maintenance_supporter/frontend/locales/hu.json index 42a5046d..c819bab9 100644 --- a/custom_components/maintenance_supporter/frontend/locales/hu.json +++ b/custom_components/maintenance_supporter/frontend/locales/hu.json @@ -212,6 +212,7 @@ "completed_at_optional": "Elvégezve ekkor (opcionális, üres = most)", "completed_at_pick": "Dátum és idő megadása", "completed_at_future_error": "Az elvégzés dátuma nem lehet a jövőben.", + "ha_time_zone_hint": "Idő a Home Assistant időzónájában ({zone})", "days": "nap", "day": "nap", "today": "Ma", @@ -556,7 +557,8 @@ "cal_forward_windows": "Jövőbeli időszakok", "history_edit_title": "Előzménybejegyzés szerkesztése", "history_delete_entry": "Bejegyzés törlése", - "history_delete_confirm": "Törli ezt az előzménybejegyzést? Az utolsó elvégzés dátuma az előzőre áll vissza; a fotók az objektumnál maradnak, a felhasznált alkatrészek nem kerülnek vissza a készletbe.", + "history_delete_confirm_entry": "Törlöd ezt az előzménybejegyzést? Az utolsó elvégzés dátuma visszaáll az előzőre; a fotók az objektumnál maradnak.", + "history_delete_returns_parts": "A felhasznált alkatrészek visszakerülnek a készletbe: {parts}.", "history_edit_timestamp": "Időbélyeg", "manufacturer": "Gyártó", "model": "Modell", @@ -855,6 +857,11 @@ "loading": "Betöltés…", "archive": "Archiválás", "undo": "Visszavonás", + "task_completed_named": "Elvégezve: {task}", + "undo_done": "Elvégzés visszavonva", + "undo_done_action_ran": "Elvégzés visszavonva. Az elvégzéskor futó művelet már lefutott, azt nem lehet visszavonni.", + "undo_nothing": "Nincs mit visszavonni. Csak a saját legutóbbi elvégzésed vonható vissza, 10 percen belül.", + "undo_changed": "Nem vonható vissza. A feladat azóta megváltozott.", "task_archived": "Feladat archiválva", "object_archived": "Objektum archiválva", "unarchive": "Archiválás visszavonása", diff --git a/custom_components/maintenance_supporter/frontend/locales/it.json b/custom_components/maintenance_supporter/frontend/locales/it.json index 49badae3..25a11695 100644 --- a/custom_components/maintenance_supporter/frontend/locales/it.json +++ b/custom_components/maintenance_supporter/frontend/locales/it.json @@ -210,6 +210,7 @@ "completed_at_optional": "Completato il (opzionale, vuoto = adesso)", "completed_at_pick": "Imposta data e ora", "completed_at_future_error": "La data di completamento non può essere nel futuro.", + "ha_time_zone_hint": "Ora nel fuso orario di Home Assistant ({zone})", "days": "giorni", "day": "giorno", "today": "Oggi", @@ -554,7 +555,8 @@ "cal_forward_windows": "Finestre future", "history_edit_title": "Modifica voce dello storico", "history_delete_entry": "Elimina voce", - "history_delete_confirm": "Eliminare questa voce della cronologia? La data dell'ultima esecuzione torna al completamento precedente; le foto restano con l'oggetto e le parti consumate non vengono rimesse a magazzino.", + "history_delete_confirm_entry": "Eliminare questa voce della cronologia? La data dell'ultima esecuzione torna a quella precedente; le foto restano con l'oggetto.", + "history_delete_returns_parts": "I ricambi usati tornano in magazzino: {parts}.", "history_edit_timestamp": "Data e ora", "manufacturer": "Produttore", "model": "Modello", @@ -848,6 +850,11 @@ "loading": "Caricamento…", "archive": "Archivia", "undo": "Annulla", + "task_completed_named": "Completato: {task}", + "undo_done": "Completamento annullato", + "undo_done_action_ran": "Completamento annullato. L'azione al completamento era già stata eseguita e non si può annullare.", + "undo_nothing": "Niente da annullare. Si può annullare solo il tuo ultimo completamento, entro 10 minuti.", + "undo_changed": "Impossibile annullare. L'attività è cambiata nel frattempo.", "task_archived": "Attività archiviata", "object_archived": "Oggetto archiviato", "unarchive": "Annulla archiviazione", diff --git a/custom_components/maintenance_supporter/frontend/locales/ja.json b/custom_components/maintenance_supporter/frontend/locales/ja.json index 26554e05..cde6c06d 100644 --- a/custom_components/maintenance_supporter/frontend/locales/ja.json +++ b/custom_components/maintenance_supporter/frontend/locales/ja.json @@ -212,6 +212,7 @@ "completed_at_optional": "完了日時(任意・空欄 = 現在)", "completed_at_pick": "日時を設定", "completed_at_future_error": "完了日時に未来は指定できません。", + "ha_time_zone_hint": "Home Assistant のタイムゾーンの時刻({zone})", "days": "日", "day": "日", "today": "今日", @@ -556,7 +557,8 @@ "cal_forward_windows": "今後の期間", "history_edit_title": "履歴項目を編集", "history_delete_entry": "記録を削除", - "history_delete_confirm": "この履歴記録を削除しますか?最終実施日は前回の完了に戻ります。写真はオブジェクトに残り、消費した部品は在庫に戻りません。", + "history_delete_confirm_entry": "この履歴記録を削除しますか?最終実施日は前回の完了に戻ります。写真はオブジェクトに残ります。", + "history_delete_returns_parts": "使用した部品は在庫に戻ります:{parts}。", "history_edit_timestamp": "日時", "manufacturer": "メーカー", "model": "型番", @@ -848,6 +850,11 @@ "loading": "読み込み中…", "archive": "アーカイブ", "undo": "元に戻す", + "task_completed_named": "完了しました:{task}", + "undo_done": "完了を取り消しました", + "undo_done_action_ran": "完了を取り消しました。完了時のアクションは既に実行されており、取り消せません。", + "undo_nothing": "取り消せるものはありません。取り消せるのは自分の直近の完了だけで、10分以内です。", + "undo_changed": "取り消せません。その後タスクが変更されています。", "task_archived": "タスクをアーカイブしました", "object_archived": "オブジェクトをアーカイブしました", "unarchive": "アーカイブ解除", diff --git a/custom_components/maintenance_supporter/frontend/locales/ko.json b/custom_components/maintenance_supporter/frontend/locales/ko.json index 4046901f..dff2c732 100644 --- a/custom_components/maintenance_supporter/frontend/locales/ko.json +++ b/custom_components/maintenance_supporter/frontend/locales/ko.json @@ -212,6 +212,7 @@ "completed_at_optional": "완료 시각 (선택, 비우면 지금)", "completed_at_pick": "날짜 및 시간 설정", "completed_at_future_error": "완료 날짜는 미래일 수 없습니다.", + "ha_time_zone_hint": "Home Assistant 시간대 기준 시간 ({zone})", "days": "일", "day": "일", "today": "오늘", @@ -556,7 +557,8 @@ "cal_forward_windows": "이후 기간", "history_edit_title": "기록 항목 편집", "history_delete_entry": "기록 삭제", - "history_delete_confirm": "이 기록을 삭제할까요? 마지막 수행일은 이전 완료로 되돌아가며, 사진은 개체에 남고 사용한 부품은 재고로 복구되지 않습니다.", + "history_delete_confirm_entry": "이 기록 항목을 삭제할까요? 마지막 수행 날짜가 이전 완료로 돌아갑니다. 사진은 객체에 남습니다.", + "history_delete_returns_parts": "사용한 부품은 재고로 돌아갑니다: {parts}.", "history_edit_timestamp": "타임스탬프", "manufacturer": "제조사", "model": "모델", @@ -855,6 +857,11 @@ "loading": "불러오는 중…", "archive": "보관", "undo": "실행 취소", + "task_completed_named": "완료됨: {task}", + "undo_done": "완료를 취소했습니다", + "undo_done_action_ran": "완료를 취소했습니다. 완료 시 동작은 이미 실행되어 되돌릴 수 없습니다.", + "undo_nothing": "취소할 항목이 없습니다. 자신의 마지막 완료만 10분 안에 취소할 수 있습니다.", + "undo_changed": "취소할 수 없습니다. 그 사이 작업이 변경되었습니다.", "task_archived": "작업이 보관되었습니다", "object_archived": "객체가 보관되었습니다", "unarchive": "보관 해제", diff --git a/custom_components/maintenance_supporter/frontend/locales/nb.json b/custom_components/maintenance_supporter/frontend/locales/nb.json index f08234ea..d0640429 100644 --- a/custom_components/maintenance_supporter/frontend/locales/nb.json +++ b/custom_components/maintenance_supporter/frontend/locales/nb.json @@ -212,6 +212,7 @@ "completed_at_optional": "Utført den (valgfritt, tomt = nå)", "completed_at_pick": "Velg dato og klokkeslett", "completed_at_future_error": "Fullføringsdatoen kan ikke ligge i fremtiden.", + "ha_time_zone_hint": "Tid i Home Assistants tidssone ({zone})", "days": "dager", "day": "dag", "today": "I dag", @@ -556,7 +557,8 @@ "cal_forward_windows": "Fremtidige vinduer", "history_edit_title": "Rediger historikkoppføring", "history_delete_entry": "Slett oppføring", - "history_delete_confirm": "Slette denne historikkoppføringen? Datoen for siste utførelse faller tilbake til den forrige; bilder blir hos objektet, og forbrukte deler legges ikke tilbake på lager.", + "history_delete_confirm_entry": "Slette denne historikkoppføringen? Datoen for siste utførelse faller tilbake til den forrige; bilder blir værende på objektet.", + "history_delete_returns_parts": "De brukte delene går tilbake på lager: {parts}.", "history_edit_timestamp": "Tidsstempel", "manufacturer": "Produsent", "model": "Modell", @@ -848,6 +850,11 @@ "loading": "Laster…", "archive": "Arkivér", "undo": "Angre", + "task_completed_named": "Fullført: {task}", + "undo_done": "Utførelsen angret", + "undo_done_action_ran": "Utførelsen angret. Handlingen ved utførelse hadde allerede kjørt og kan ikke angres.", + "undo_nothing": "Ingenting å angre. Bare din egen siste utførelse kan angres, innen 10 minutter.", + "undo_changed": "Kan ikke angres. Oppgaven er endret siden.", "task_archived": "Oppgave arkivert", "object_archived": "Objekt arkivert", "unarchive": "Gjenopprett", diff --git a/custom_components/maintenance_supporter/frontend/locales/nl.json b/custom_components/maintenance_supporter/frontend/locales/nl.json index 1e7d5bae..82b507ac 100644 --- a/custom_components/maintenance_supporter/frontend/locales/nl.json +++ b/custom_components/maintenance_supporter/frontend/locales/nl.json @@ -210,6 +210,7 @@ "completed_at_optional": "Voltooid op (optioneel, leeg = nu)", "completed_at_pick": "Datum & tijd kiezen", "completed_at_future_error": "De voltooiingsdatum mag niet in de toekomst liggen.", + "ha_time_zone_hint": "Tijd in de tijdzone van Home Assistant ({zone})", "days": "dagen", "day": "dag", "today": "Vandaag", @@ -554,7 +555,8 @@ "cal_forward_windows": "Toekomstige vensters", "history_edit_title": "Geschiedenisitem bewerken", "history_delete_entry": "Vermelding verwijderen", - "history_delete_confirm": "Deze geschiedenisvermelding verwijderen? De datum van laatste uitvoering valt terug op de vorige voltooiing; foto's blijven bij het object en verbruikte onderdelen worden niet teruggeboekt.", + "history_delete_confirm_entry": "Deze geschiedenisregel verwijderen? De datum van de laatste uitvoering valt terug op de vorige; foto's blijven bij het object.", + "history_delete_returns_parts": "De gebruikte onderdelen gaan terug naar de voorraad: {parts}.", "history_edit_timestamp": "Tijdstempel", "manufacturer": "Fabrikant", "model": "Model", @@ -848,6 +850,11 @@ "loading": "Laden…", "archive": "Archiveren", "undo": "Ongedaan maken", + "task_completed_named": "Voltooid: {task}", + "undo_done": "Afronding ongedaan gemaakt", + "undo_done_action_ran": "Afronding ongedaan gemaakt. De actie bij afronden was al uitgevoerd en kan niet worden teruggedraaid.", + "undo_nothing": "Niets om ongedaan te maken. Alleen je eigen laatste afronding kan worden teruggedraaid, binnen 10 minuten.", + "undo_changed": "Ongedaan maken kan niet. De taak is sindsdien gewijzigd.", "task_archived": "Taak gearchiveerd", "object_archived": "Object gearchiveerd", "unarchive": "Dearchiveren", diff --git a/custom_components/maintenance_supporter/frontend/locales/pl.json b/custom_components/maintenance_supporter/frontend/locales/pl.json index 6b9047ac..e7d7b605 100644 --- a/custom_components/maintenance_supporter/frontend/locales/pl.json +++ b/custom_components/maintenance_supporter/frontend/locales/pl.json @@ -210,6 +210,7 @@ "completed_at_optional": "Wykonano dnia (opcjonalne, puste = teraz)", "completed_at_pick": "Ustaw datę i godzinę", "completed_at_future_error": "Data wykonania nie może być w przyszłości.", + "ha_time_zone_hint": "Czas w strefie czasowej Home Assistant ({zone})", "days": "dni", "day": "dzień", "today": "Dzisiaj", @@ -554,7 +555,8 @@ "cal_forward_windows": "Przyszłe okna", "history_edit_title": "Edytuj wpis historii", "history_delete_entry": "Usuń wpis", - "history_delete_confirm": "Usunąć ten wpis historii? Data ostatniego wykonania cofnie się do poprzedniego; zdjęcia pozostaną przy obiekcie, a zużyte części nie wrócą na stan.", + "history_delete_confirm_entry": "Usunąć ten wpis historii? Data ostatniego wykonania wróci do poprzedniej; zdjęcia zostaną przy obiekcie.", + "history_delete_returns_parts": "Zużyte części wrócą do magazynu: {parts}.", "history_edit_timestamp": "Znacznik czasu", "manufacturer": "Producent", "model": "Model", @@ -848,6 +850,11 @@ "loading": "Ładowanie…", "archive": "Archiwizuj", "undo": "Cofnij", + "task_completed_named": "Wykonano: {task}", + "undo_done": "Wykonanie cofnięte", + "undo_done_action_ran": "Wykonanie cofnięte. Akcja przy wykonaniu już się odbyła i nie da się jej cofnąć.", + "undo_nothing": "Nie ma czego cofnąć. Cofnąć można tylko swoje ostatnie wykonanie, w ciągu 10 minut.", + "undo_changed": "Nie można cofnąć. Zadanie zmieniło się w międzyczasie.", "task_archived": "Zadanie zarchiwizowane", "object_archived": "Obiekt zarchiwizowany", "unarchive": "Przywróć z archiwum", diff --git a/custom_components/maintenance_supporter/frontend/locales/pt-br.json b/custom_components/maintenance_supporter/frontend/locales/pt-br.json index b1ab140c..03c1a270 100644 --- a/custom_components/maintenance_supporter/frontend/locales/pt-br.json +++ b/custom_components/maintenance_supporter/frontend/locales/pt-br.json @@ -212,6 +212,7 @@ "completed_at_optional": "Concluído em (opcional, vazio = agora)", "completed_at_pick": "Definir data e hora", "completed_at_future_error": "A data de conclusão não pode estar no futuro.", + "ha_time_zone_hint": "Horário no fuso horário do Home Assistant ({zone})", "days": "dias", "day": "dia", "today": "Hoje", @@ -556,7 +557,8 @@ "cal_forward_windows": "Janelas futuras", "history_edit_title": "Editar entrada do histórico", "history_delete_entry": "Excluir entrada", - "history_delete_confirm": "Excluir esta entrada do histórico? A data da última execução volta para a conclusão anterior; as fotos permanecem com o objeto e as peças consumidas não são repostas.", + "history_delete_confirm_entry": "Excluir esta entrada do histórico? A data da última realização volta para a anterior; as fotos continuam com o objeto.", + "history_delete_returns_parts": "As peças usadas voltam ao estoque: {parts}.", "history_edit_timestamp": "Data e hora", "manufacturer": "Fabricante", "model": "Modelo", @@ -855,6 +857,11 @@ "loading": "Carregando…", "archive": "Arquivar", "undo": "Desfazer", + "task_completed_named": "Concluída: {task}", + "undo_done": "Conclusão desfeita", + "undo_done_action_ran": "Conclusão desfeita. A ação de conclusão já tinha sido executada e não pode ser revertida.", + "undo_nothing": "Nada para desfazer. Só a sua última conclusão pode ser desfeita, em até 10 minutos.", + "undo_changed": "Não é possível desfazer. A tarefa mudou desde então.", "task_archived": "Tarefa arquivada", "object_archived": "Objeto arquivado", "unarchive": "Desarquivar", diff --git a/custom_components/maintenance_supporter/frontend/locales/pt.json b/custom_components/maintenance_supporter/frontend/locales/pt.json index c75a730a..35d58bf1 100644 --- a/custom_components/maintenance_supporter/frontend/locales/pt.json +++ b/custom_components/maintenance_supporter/frontend/locales/pt.json @@ -210,6 +210,7 @@ "completed_at_optional": "Concluído em (opcional, vazio = agora)", "completed_at_pick": "Definir data e hora", "completed_at_future_error": "A data de conclusão não pode estar no futuro.", + "ha_time_zone_hint": "Hora no fuso horário do Home Assistant ({zone})", "days": "dias", "day": "dia", "today": "Hoje", @@ -554,7 +555,8 @@ "cal_forward_windows": "Janelas futuras", "history_edit_title": "Editar entrada do histórico", "history_delete_entry": "Eliminar entrada", - "history_delete_confirm": "Eliminar esta entrada do histórico? A data da última execução recua para a conclusão anterior; as fotos ficam com o objeto e as peças consumidas não são repostas.", + "history_delete_confirm_entry": "Eliminar esta entrada do histórico? A data da última realização volta à anterior; as fotos ficam com o objeto.", + "history_delete_returns_parts": "As peças usadas voltam ao stock: {parts}.", "history_edit_timestamp": "Carimbo de data/hora", "manufacturer": "Fabricante", "model": "Modelo", @@ -848,6 +850,11 @@ "loading": "A carregar…", "archive": "Arquivar", "undo": "Desfazer", + "task_completed_named": "Concluída: {task}", + "undo_done": "Conclusão anulada", + "undo_done_action_ran": "Conclusão anulada. A ação de conclusão já tinha sido executada e não pode ser revertida.", + "undo_nothing": "Nada para anular. Só a sua última conclusão pode ser anulada, dentro de 10 minutos.", + "undo_changed": "Não é possível anular. A tarefa mudou entretanto.", "task_archived": "Tarefa arquivada", "object_archived": "Objeto arquivado", "unarchive": "Desarquivar", diff --git a/custom_components/maintenance_supporter/frontend/locales/ru.json b/custom_components/maintenance_supporter/frontend/locales/ru.json index d5671a57..30823484 100644 --- a/custom_components/maintenance_supporter/frontend/locales/ru.json +++ b/custom_components/maintenance_supporter/frontend/locales/ru.json @@ -210,6 +210,7 @@ "completed_at_optional": "Выполнено (необязательно, пусто = сейчас)", "completed_at_pick": "Указать дату и время", "completed_at_future_error": "Дата выполнения не может быть в будущем.", + "ha_time_zone_hint": "Время в часовом поясе Home Assistant ({zone})", "days": "дней", "day": "день", "today": "Сегодня", @@ -554,7 +555,8 @@ "cal_forward_windows": "Будущие окна", "history_edit_title": "Редактировать запись истории", "history_delete_entry": "Удалить запись", - "history_delete_confirm": "Удалить эту запись истории? Дата последнего выполнения вернётся к предыдущему; фотографии останутся у объекта, а израсходованные детали не будут возвращены на склад.", + "history_delete_confirm_entry": "Удалить эту запись истории? Дата последнего выполнения вернётся к предыдущей; фото останутся у объекта.", + "history_delete_returns_parts": "Использованные запчасти вернутся на склад: {parts}.", "history_edit_timestamp": "Метка времени", "manufacturer": "Производитель", "model": "Модель", @@ -848,6 +850,11 @@ "loading": "Загрузка…", "archive": "Архивировать", "undo": "Отменить", + "task_completed_named": "Выполнено: {task}", + "undo_done": "Выполнение отменено", + "undo_done_action_ran": "Выполнение отменено. Действие при выполнении уже сработало, его нельзя отменить.", + "undo_nothing": "Нечего отменять. Можно отменить только своё последнее выполнение, в течение 10 минут.", + "undo_changed": "Отменить нельзя. Задача с тех пор изменилась.", "task_archived": "Задача архивирована", "object_archived": "Объект архивирован", "unarchive": "Разархивировать", diff --git a/custom_components/maintenance_supporter/frontend/locales/sv.json b/custom_components/maintenance_supporter/frontend/locales/sv.json index 477c6fec..2ed19dbc 100644 --- a/custom_components/maintenance_supporter/frontend/locales/sv.json +++ b/custom_components/maintenance_supporter/frontend/locales/sv.json @@ -210,6 +210,7 @@ "completed_at_optional": "Utförd den (valfritt, tomt = nu)", "completed_at_pick": "Ange datum och tid", "completed_at_future_error": "Slutförandedatumet får inte ligga i framtiden.", + "ha_time_zone_hint": "Tid i Home Assistants tidszon ({zone})", "days": "dagar", "day": "dag", "today": "Idag", @@ -554,7 +555,8 @@ "cal_forward_windows": "Kommande fönster", "history_edit_title": "Redigera historikpost", "history_delete_entry": "Ta bort post", - "history_delete_confirm": "Ta bort den här historikposten? Datumet för senaste utförande faller tillbaka till det föregående; foton stannar hos objektet och förbrukade delar läggs inte tillbaka i lager.", + "history_delete_confirm_entry": "Ta bort den här historikposten? Datumet för senaste utförandet faller tillbaka på det föregående; foton finns kvar på objektet.", + "history_delete_returns_parts": "De använda delarna går tillbaka till lagret: {parts}.", "history_edit_timestamp": "Tidsstämpel", "manufacturer": "Tillverkare", "model": "Modell", @@ -848,6 +850,11 @@ "loading": "Laddar…", "archive": "Arkivera", "undo": "Ångra", + "task_completed_named": "Slutförd: {task}", + "undo_done": "Utförandet ångrat", + "undo_done_action_ran": "Utförandet ångrat. Åtgärden vid utförande hade redan körts och kan inte ångras.", + "undo_nothing": "Inget att ångra. Bara ditt eget senaste utförande kan ångras, inom 10 minuter.", + "undo_changed": "Kan inte ångras. Uppgiften har ändrats sedan dess.", "task_archived": "Uppgift arkiverad", "object_archived": "Objekt arkiverat", "unarchive": "Återställ", diff --git a/custom_components/maintenance_supporter/frontend/locales/tr.json b/custom_components/maintenance_supporter/frontend/locales/tr.json index 3519d0fb..26f2d7af 100644 --- a/custom_components/maintenance_supporter/frontend/locales/tr.json +++ b/custom_components/maintenance_supporter/frontend/locales/tr.json @@ -212,6 +212,7 @@ "completed_at_optional": "Tamamlanma zamanı (isteğe bağlı, boş = şimdi)", "completed_at_pick": "Tarih ve saat seç", "completed_at_future_error": "Tamamlanma tarihi gelecekte olamaz.", + "ha_time_zone_hint": "Home Assistant saat dilimindeki saat ({zone})", "days": "gün", "day": "gün", "today": "Bugün", @@ -556,7 +557,8 @@ "cal_forward_windows": "Gelecek dönemler", "history_edit_title": "Geçmiş kaydını düzenle", "history_delete_entry": "Kaydı sil", - "history_delete_confirm": "Bu geçmiş kaydı silinsin mi? Son yapılma tarihi bir önceki tamamlamaya döner; fotoğraflar nesnede kalır, kullanılan parçalar stoğa geri eklenmez.", + "history_delete_confirm_entry": "Bu geçmiş kaydı silinsin mi? Son yapılma tarihi bir önceki tamamlamaya geri döner; fotoğraflar nesnede kalır.", + "history_delete_returns_parts": "Kullanılan parçalar stoğa geri döner: {parts}.", "history_edit_timestamp": "Zaman damgası", "manufacturer": "Üretici", "model": "Model", @@ -855,6 +857,11 @@ "loading": "Yükleniyor…", "archive": "Arşivle", "undo": "Geri al", + "task_completed_named": "Tamamlandı: {task}", + "undo_done": "Tamamlama geri alındı", + "undo_done_action_ran": "Tamamlama geri alındı. Tamamlama eylemi zaten çalışmıştı ve geri alınamaz.", + "undo_nothing": "Geri alınacak bir şey yok. Yalnızca kendi son tamamlamanızı 10 dakika içinde geri alabilirsiniz.", + "undo_changed": "Geri alınamıyor. Görev o zamandan beri değişti.", "task_archived": "Görev arşivlendi", "object_archived": "Nesne arşivlendi", "unarchive": "Arşivden çıkar", diff --git a/custom_components/maintenance_supporter/frontend/locales/uk.json b/custom_components/maintenance_supporter/frontend/locales/uk.json index 1c4d3a8b..e013ceb3 100644 --- a/custom_components/maintenance_supporter/frontend/locales/uk.json +++ b/custom_components/maintenance_supporter/frontend/locales/uk.json @@ -210,6 +210,7 @@ "completed_at_optional": "Виконано (необов'язково, порожньо = зараз)", "completed_at_pick": "Вказати дату й час", "completed_at_future_error": "Дата виконання не може бути в майбутньому.", + "ha_time_zone_hint": "Час у часовому поясі Home Assistant ({zone})", "days": "днів", "day": "день", "today": "Сьогодні", @@ -554,7 +555,8 @@ "cal_forward_windows": "Майбутні вікна", "history_edit_title": "Редагувати запис історії", "history_delete_entry": "Видалити запис", - "history_delete_confirm": "Видалити цей запис історії? Дата останнього виконання повернеться до попереднього; фотографії залишаться в об'єкта, а витрачені деталі не повернуться на склад.", + "history_delete_confirm_entry": "Видалити цей запис історії? Дата останнього виконання повернеться до попередньої; фото залишаться в об'єкта.", + "history_delete_returns_parts": "Використані запчастини повернуться на склад: {parts}.", "history_edit_timestamp": "Позначка часу", "manufacturer": "Виробник", "model": "Модель", @@ -848,6 +850,11 @@ "loading": "Завантаження…", "archive": "Архівувати", "undo": "Скасувати", + "task_completed_named": "Виконано: {task}", + "undo_done": "Виконання скасовано", + "undo_done_action_ran": "Виконання скасовано. Дія під час виконання вже спрацювала, її не можна скасувати.", + "undo_nothing": "Нічого скасовувати. Можна скасувати лише своє останнє виконання, протягом 10 хвилин.", + "undo_changed": "Скасувати не можна. Завдання відтоді змінилося.", "task_archived": "Завдання архівовано", "object_archived": "Об'єкт архівовано", "unarchive": "Розархівувати", diff --git a/custom_components/maintenance_supporter/frontend/locales/zh.json b/custom_components/maintenance_supporter/frontend/locales/zh.json index e138561b..64d4d24a 100644 --- a/custom_components/maintenance_supporter/frontend/locales/zh.json +++ b/custom_components/maintenance_supporter/frontend/locales/zh.json @@ -212,6 +212,7 @@ "completed_at_optional": "完成时间(可选,留空 = 现在)", "completed_at_pick": "设置日期和时间", "completed_at_future_error": "完成日期不能是未来时间。", + "ha_time_zone_hint": "Home Assistant 时区的时间({zone})", "days": "天", "day": "天", "today": "今天", @@ -556,7 +557,8 @@ "cal_forward_windows": "未来的窗口", "history_edit_title": "编辑历史记录", "history_delete_entry": "删除记录", - "history_delete_confirm": "删除此历史记录?上次完成日期将回退到前一次完成;照片仍保留在对象中,已消耗的配件不会退回库存。", + "history_delete_confirm_entry": "删除此历史记录?上次执行日期将退回到上一次完成;照片仍保留在对象中。", + "history_delete_returns_parts": "所用的配件将退回库存:{parts}。", "history_edit_timestamp": "时间戳", "manufacturer": "制造商", "model": "型号", @@ -848,6 +850,11 @@ "loading": "加载中…", "archive": "归档", "undo": "撤销", + "task_completed_named": "已完成:{task}", + "undo_done": "已撤销完成", + "undo_done_action_ran": "已撤销完成。完成时的操作已经执行,无法撤销。", + "undo_nothing": "没有可撤销的内容。只能在 10 分钟内撤销你自己最近一次完成。", + "undo_changed": "无法撤销。此后任务已发生变化。", "task_archived": "任务已归档", "object_archived": "对象已归档", "unarchive": "取消归档", diff --git a/custom_components/maintenance_supporter/frontend/maintenance-calendar-card.js b/custom_components/maintenance_supporter/frontend/maintenance-calendar-card.js index 7471f110..4f1ca2eb 100644 --- a/custom_components/maintenance_supporter/frontend/maintenance-calendar-card.js +++ b/custom_components/maintenance_supporter/frontend/maintenance-calendar-card.js @@ -1,9 +1,9 @@ -/*! maintenance_supporter frontend 2.97.1 */ -var tr=Object.defineProperty;var Vn=Object.getOwnPropertyDescriptor;var y=(s,r,e)=>()=>{if(e)throw e[0];try{return s&&(r=s(s=0)),r}catch(t){throw e=[t],t}};var Gn=(s,r)=>{for(var e in r)tr(s,e,{get:r[e],enumerable:!0})};var c=(s,r,e,t)=>{for(var i=t>1?void 0:t?Vn(r,e):r,a=s.length-1,o;a>=0;a--)(o=s[a])&&(i=(t?o(r,e,i):o(i))||i);return t&&i&&tr(r,e,i),i};var $t,Et,ai,ir,rt,rr,E,sr,oi,li=y(()=>{$t=globalThis,Et=$t.ShadowRoot&&($t.ShadyCSS===void 0||$t.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,ai=Symbol(),ir=new WeakMap,rt=class{constructor(r,e,t){if(this._$cssResult$=!0,t!==ai)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=r,this.t=e}get styleSheet(){let r=this.o,e=this.t;if(Et&&r===void 0){let t=e!==void 0&&e.length===1;t&&(r=ir.get(e)),r===void 0&&((this.o=r=new CSSStyleSheet).replaceSync(this.cssText),t&&ir.set(e,r))}return r}toString(){return this.cssText}},rr=s=>new rt(typeof s=="string"?s:s+"",void 0,ai),E=(s,...r)=>{let e=s.length===1?s[0]:r.reduce((t,i,a)=>t+(o=>{if(o._$cssResult$===!0)return o.cssText;if(typeof o=="number")return o;throw Error("Value passed to 'css' function must be a 'css' function result: "+o+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+s[a+1],s[0]);return new rt(e,s,ai)},sr=(s,r)=>{if(Et)s.adoptedStyleSheets=r.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let e of r){let t=document.createElement("style"),i=$t.litNonce;i!==void 0&&t.setAttribute("nonce",i),t.textContent=e.cssText,s.appendChild(t)}},oi=Et?s=>s:s=>s instanceof CSSStyleSheet?(r=>{let e="";for(let t of r.cssRules)e+=t.cssText;return rr(e)})(s):s});var Kn,Yn,Qn,Jn,Zn,Xn,St,nr,ea,ta,st,nt,Tt,ar,ne,at=y(()=>{li();li();({is:Kn,defineProperty:Yn,getOwnPropertyDescriptor:Qn,getOwnPropertyNames:Jn,getOwnPropertySymbols:Zn,getPrototypeOf:Xn}=Object),St=globalThis,nr=St.trustedTypes,ea=nr?nr.emptyScript:"",ta=St.reactiveElementPolyfillSupport,st=(s,r)=>s,nt={toAttribute(s,r){switch(r){case Boolean:s=s?ea:null;break;case Object:case Array:s=s==null?s:JSON.stringify(s)}return s},fromAttribute(s,r){let e=s;switch(r){case Boolean:e=s!==null;break;case Number:e=s===null?null:Number(s);break;case Object:case Array:try{e=JSON.parse(s)}catch{e=null}}return e}},Tt=(s,r)=>!Kn(s,r),ar={attribute:!0,type:String,converter:nt,reflect:!1,useDefault:!1,hasChanged:Tt};Symbol.metadata??=Symbol("metadata"),St.litPropertyMetadata??=new WeakMap;ne=class extends HTMLElement{static addInitializer(r){this._$Ei(),(this.l??=[]).push(r)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(r,e=ar){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(r)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(r,e),!e.noAccessor){let t=Symbol(),i=this.getPropertyDescriptor(r,t,e);i!==void 0&&Yn(this.prototype,r,i)}}static getPropertyDescriptor(r,e,t){let{get:i,set:a}=Qn(this.prototype,r)??{get(){return this[e]},set(o){this[e]=o}};return{get:i,set(o){let d=i?.call(this);a?.call(this,o),this.requestUpdate(r,d,t)},configurable:!0,enumerable:!0}}static getPropertyOptions(r){return this.elementProperties.get(r)??ar}static _$Ei(){if(this.hasOwnProperty(st("elementProperties")))return;let r=Xn(this);r.finalize(),r.l!==void 0&&(this.l=[...r.l]),this.elementProperties=new Map(r.elementProperties)}static finalize(){if(this.hasOwnProperty(st("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(st("properties"))){let e=this.properties,t=[...Jn(e),...Zn(e)];for(let i of t)this.createProperty(i,e[i])}let r=this[Symbol.metadata];if(r!==null){let e=litPropertyMetadata.get(r);if(e!==void 0)for(let[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let i=this._$Eu(e,t);i!==void 0&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(r){let e=[];if(Array.isArray(r)){let t=new Set(r.flat(1/0).reverse());for(let i of t)e.unshift(oi(i))}else r!==void 0&&e.push(oi(r));return e}static _$Eu(r,e){let t=e.attribute;return t===!1?void 0:typeof t=="string"?t:typeof r=="string"?r.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(r=>this.enableUpdating=r),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(r=>r(this))}addController(r){(this._$EO??=new Set).add(r),this.renderRoot!==void 0&&this.isConnected&&r.hostConnected?.()}removeController(r){this._$EO?.delete(r)}_$E_(){let r=new Map,e=this.constructor.elementProperties;for(let t of e.keys())this.hasOwnProperty(t)&&(r.set(t,this[t]),delete this[t]);r.size>0&&(this._$Ep=r)}createRenderRoot(){let r=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return sr(r,this.constructor.elementStyles),r}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(r=>r.hostConnected?.())}enableUpdating(r){}disconnectedCallback(){this._$EO?.forEach(r=>r.hostDisconnected?.())}attributeChangedCallback(r,e,t){this._$AK(r,t)}_$ET(r,e){let t=this.constructor.elementProperties.get(r),i=this.constructor._$Eu(r,t);if(i!==void 0&&t.reflect===!0){let a=(t.converter?.toAttribute!==void 0?t.converter:nt).toAttribute(e,t.type);this._$Em=r,a==null?this.removeAttribute(i):this.setAttribute(i,a),this._$Em=null}}_$AK(r,e){let t=this.constructor,i=t._$Eh.get(r);if(i!==void 0&&this._$Em!==i){let a=t.getPropertyOptions(i),o=typeof a.converter=="function"?{fromAttribute:a.converter}:a.converter?.fromAttribute!==void 0?a.converter:nt;this._$Em=i;let d=o.fromAttribute(e,a.type);this[i]=d??this._$Ej?.get(i)??d,this._$Em=null}}requestUpdate(r,e,t,i=!1,a){if(r!==void 0){let o=this.constructor;if(i===!1&&(a=this[r]),t??=o.getPropertyOptions(r),!((t.hasChanged??Tt)(a,e)||t.useDefault&&t.reflect&&a===this._$Ej?.get(r)&&!this.hasAttribute(o._$Eu(r,t))))return;this.C(r,e,t)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(r,e,{useDefault:t,reflect:i,wrapped:a},o){t&&!(this._$Ej??=new Map).has(r)&&(this._$Ej.set(r,o??e??this[r]),a!==!0||o!==void 0)||(this._$AL.has(r)||(this.hasUpdated||t||(e=void 0),this._$AL.set(r,e)),i===!0&&this._$Em!==r&&(this._$Eq??=new Set).add(r))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}let r=this.scheduleUpdate();return r!=null&&await r,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(let[i,a]of this._$Ep)this[i]=a;this._$Ep=void 0}let t=this.constructor.elementProperties;if(t.size>0)for(let[i,a]of t){let{wrapped:o}=a,d=this[i];o!==!0||this._$AL.has(i)||d===void 0||this.C(i,void 0,a,d)}}let r=!1,e=this._$AL;try{r=this.shouldUpdate(e),r?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(t){throw r=!1,this._$EM(),t}r&&this._$AE(e)}willUpdate(r){}_$AE(r){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(r)),this.updated(r)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(r){return!0}update(r){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(r){}firstUpdated(r){}};ne.elementStyles=[],ne.shadowRootOptions={mode:"open"},ne[st("elementProperties")]=new Map,ne[st("finalized")]=new Map,ta?.({ReactiveElement:ne}),(St.reactiveElementVersions??=[]).push("2.1.2")});function fr(s,r){if(!gi(s)||!s.hasOwnProperty("raw"))throw Error("invalid template strings array");return lr!==void 0?lr.createHTML(r):r}function je(s,r,e=s,t){if(r===$e)return r;let i=t!==void 0?e._$Co?.[t]:e._$Cl,a=ct(r)?void 0:r._$litDirective$;return i?.constructor!==a&&(i?._$AO?.(!1),a===void 0?i=void 0:(i=new a(s),i._$AT(s,e,t)),t!==void 0?(e._$Co??=[])[t]=i:e._$Cl=i),i!==void 0&&(r=je(s,i._$AS(s,r.values),i,t)),r}var mi,or,At,lr,_r,ue,mr,ia,ke,lt,ct,gi,ra,ci,ot,cr,dr,xe,pr,ur,gr,fi,l,De,Ko,$e,h,hr,we,sa,dt,di,pt,Oe,pi,ui,hi,_i,na,vr,Ct=y(()=>{mi=globalThis,or=s=>s,At=mi.trustedTypes,lr=At?At.createPolicy("lit-html",{createHTML:s=>s}):void 0,_r="$lit$",ue=`lit$${Math.random().toFixed(9).slice(2)}$`,mr="?"+ue,ia=`<${mr}>`,ke=document,lt=()=>ke.createComment(""),ct=s=>s===null||typeof s!="object"&&typeof s!="function",gi=Array.isArray,ra=s=>gi(s)||typeof s?.[Symbol.iterator]=="function",ci=`[ -\f\r]`,ot=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,cr=/-->/g,dr=/>/g,xe=RegExp(`>|${ci}(?:([^\\s"'>=/]+)(${ci}*=${ci}*(?:[^ -\f\r"'\`<>=]|("|')|))|$)`,"g"),pr=/'/g,ur=/"/g,gr=/^(?:script|style|textarea|title)$/i,fi=s=>(r,...e)=>({_$litType$:s,strings:r,values:e}),l=fi(1),De=fi(2),Ko=fi(3),$e=Symbol.for("lit-noChange"),h=Symbol.for("lit-nothing"),hr=new WeakMap,we=ke.createTreeWalker(ke,129);sa=(s,r)=>{let e=s.length-1,t=[],i,a=r===2?"":r===3?"":"",o=ot;for(let d=0;d"?(o=i??ot,f=-1):m[1]===void 0?f=-2:(f=o.lastIndex-m[2].length,_=m[1],o=m[3]===void 0?xe:m[3]==='"'?ur:pr):o===ur||o===pr?o=xe:o===cr||o===dr?o=ot:(o=xe,i=void 0);let w=o===xe&&s[d+1].startsWith("/>")?" ":"";a+=o===ot?u+ia:f>=0?(t.push(_),u.slice(0,f)+_r+u.slice(f)+ue+w):u+ue+(f===-2?d:w)}return[fr(s,a+(s[e]||"")+(r===2?"":r===3?"":"")),t]},dt=class s{constructor({strings:r,_$litType$:e},t){let i;this.parts=[];let a=0,o=0,d=r.length-1,u=this.parts,[_,m]=sa(r,e);if(this.el=s.createElement(_,t),we.currentNode=this.el.content,e===2||e===3){let f=this.el.content.firstChild;f.replaceWith(...f.childNodes)}for(;(i=we.nextNode())!==null&&u.length0){i.textContent=At?At.emptyScript:"";for(let w=0;w2||t[0]!==""||t[1]!==""?(this._$AH=Array(t.length-1).fill(new String),this.strings=t):this._$AH=h}_$AI(r,e=this,t,i){let a=this.strings,o=!1;if(a===void 0)r=je(this,r,e,0),o=!ct(r)||r!==this._$AH&&r!==$e,o&&(this._$AH=r);else{let d=r,u,_;for(r=a[0],u=0;u{let t=e?.renderBefore??r,i=t._$litPart$;if(i===void 0){let a=e?.renderBefore??null;t._$litPart$=i=new pt(r.insertBefore(lt(),a),a,void 0,e??{})}return i._$AI(s),i}});var vi,S,aa,yr=y(()=>{at();at();Ct();Ct();vi=globalThis,S=class extends ne{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){let r=super.createRenderRoot();return this.renderOptions.renderBefore??=r.firstChild,r}update(r){let e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(r),this._$Do=vr(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return $e}};S._$litElement$=!0,S.finalized=!0,vi.litElementHydrateSupport?.({LitElement:S});aa=vi.litElementPolyfillSupport;aa?.({LitElement:S});(vi.litElementVersions??=[]).push("4.2.2")});var br=y(()=>{});var C=y(()=>{at();Ct();yr();br()});var xr=y(()=>{});function v(s){return(r,e)=>typeof e=="object"?la(s,r,e):((t,i,a)=>{let o=i.hasOwnProperty(a);return i.constructor.createProperty(a,t),o?Object.getOwnPropertyDescriptor(i,a):void 0})(s,r,e)}var oa,la,yi=y(()=>{at();oa={attribute:!0,type:String,converter:nt,reflect:!1,hasChanged:Tt},la=(s=oa,r,e)=>{let{kind:t,metadata:i}=e,a=globalThis.litPropertyMetadata.get(i);if(a===void 0&&globalThis.litPropertyMetadata.set(i,a=new Map),t==="setter"&&((s=Object.create(s)).wrapped=!0),a.set(e.name,s),t==="accessor"){let{name:o}=e;return{set(d){let u=r.get.call(this);r.set.call(this,d),this.requestUpdate(o,u,s,!0,d)},init(d){return d!==void 0&&this.C(o,void 0,s,d),d}}}if(t==="setter"){let{name:o}=e;return function(d){let u=this[o];r.call(this,d),this.requestUpdate(o,u,s,!0,d)}}throw Error("Unsupported decorator location: "+t)}});function p(s){return v({...s,state:!0,attribute:!1})}var wr=y(()=>{yi();});var kr=y(()=>{});var Me=y(()=>{});var $r=y(()=>{Me();});var Er=y(()=>{Me();});var Sr=y(()=>{Me();});var Tr=y(()=>{Me();});var Ar=y(()=>{Me();});var z=y(()=>{xr();yi();wr();kr();$r();Er();Sr();Tr();Ar()});function Cr(s){return s==="use"?"use":"purchase"}function Ir(s){let r=Rt(s?.cost);if(r===null)return null;let e=Rt(s?.package_size);return e&&e>0?r/e:r}function Pt(s){let r=Rt(s.cost);if(s.cost_basis!=="use")return r;let e=Rt(s.parts_cost),t=s.purchase?null:r;return t===null&&e===null?null:(t??0)+(e??0)}var Rt,Lt=y(()=>{"use strict";Rt=s=>typeof s=="number"&&Number.isFinite(s)?s:null});function Rr(s,r){return!s||s<=0?0:s*(ca[r||"days"]??1)}var ca,Pr=y(()=>{"use strict";ca={days:1,weeks:7,months:30.4368,years:365.25}});function xi(s){return da[s??""]??Lr.length}var bi,Lr,da,ut,Nt=y(()=>{"use strict";bi={ok:"var(--success-color, #4caf50)",due_soon:"var(--warning-color, #ff9800)",overdue:"var(--error-color, #f44336)",triggered:"var(--deep-orange-color, #ff5722)",archived:"var(--disabled-color, #9e9e9e)",paused:"var(--info-color, #2196f3)"},Lr=["overdue","triggered","due_soon","ok"],da=Object.fromEntries(Lr.map((s,r)=>[s,r]));ut={ok:"mdi:check-circle",due_soon:"mdi:alert-circle",overdue:"mdi:alert-octagon",triggered:"mdi:bell-alert",archived:"mdi:archive-outline",paused:"mdi:pause-circle-outline",completed:"mdi:check-circle",skipped:"mdi:skip-next",missed:"mdi:calendar-remove",reset:"mdi:refresh"}});function he(s){let r=s.getFullYear(),e=String(s.getMonth()+1).padStart(2,"0"),t=String(s.getDate()).padStart(2,"0");return`${r}-${e}-${t}`}function qr(s){let r=String(s.getHours()).padStart(2,"0"),e=String(s.getMinutes()).padStart(2,"0");return`${he(s)}T${r}:${e}:00`}function pa(s,r){let e=[];for(let t=0;tPt(e)).filter(e=>e!==null&&e>=0);return r.length===0?null:r.reduce((e,t)=>e+t,0)/r.length}function ha(s){let{windowStart:r,windowEnd:e,task:t,entryId:i,objectName:a}=s,o=[],d=(f,b)=>({date:f,entry_id:i,task_id:t.id,task_name:t.name,object_name:a,status:b&&(t.status==="overdue"||t.status==="triggered")?"ok":t.status,days_until_due:b?null:t.days_until_due??null,projected:b,schedule_type:t.schedule_type,interval_days:t.interval_days??null,interval_unit:t.interval_unit??null,responsible_user_id:t.responsible_user_id??null,avg_cost:ua(t.history),adaptive_enabled:!!t.adaptive_config?.enabled,prediction_confidence:t.threshold_prediction_confidence??null}),u=Math.max(1,Math.round(Rr(t.interval_days,t.interval_unit)));if(t.status==="overdue"||t.status==="triggered"){if(o.push(d(r,!1)),t.schedule_type==="time_based"&&t.interval_days&&t.interval_days>0){let f=qt(r,u),b=1;for(;f<=e&&b=r&&m<=e)o.push(d(m,!1));else if(m>e)return o;if(t.schedule_type==="time_based"&&t.interval_days&&t.interval_days>0){let f=qt(m,u),b=o.length;for(;f<=e&&b=r&&(o.push(d(f,!0)),b++),f=qt(f,u)}return o}function Hr(s,r,e,t=null){let i=pa(r,e),a=i[0],o=i[i.length-1],d=[];for(let _ of s){let m=_.object?.name||"",f=_.entry_id,b=_.tasks||[];for(let w of b){if(t&&w.responsible_user_id!==t||w.enabled===!1)continue;let k=ha({windowStart:a,windowEnd:o,task:w,entryId:f,objectName:m});d.push(...k)}}let u=new Map;for(let _ of i)u.set(_,[]);for(let _ of d){let m=u.get(_.date);m&&m.push(_)}for(let[,_]of u)_.sort((m,f)=>{let b=xi(m.status),w=xi(f.status);if(b!==w)return b-w;if(m.projected!==f.projected)return m.projected?1:-1;let k=m.object_name.localeCompare(f.object_name);return k!==0?k:m.task_name.localeCompare(f.task_name)});return i.map(_=>({date:_,events:u.get(_)??[]}))}function Ht(s,r){return`${s}/${r}`}function jr(s,r,e){let t=Or(r,e)[0],i=[];for(let a of s)for(let o of a.tasks||[]){let d=o.history||[],u=o.history_count??d.length;if(u<=d.length)continue;let _=d.map(b=>typeof b?.timestamp=="string"?b.timestamp:"").filter(Boolean),m=_.length?_.reduce((b,w)=>b>>0).toString(36)}function Or(s,r){let e=[];for(let t=r-1;t>=0;t--){let i=new Date(s);i.setDate(i.getDate()-t),i.setHours(0,0,0,0),e.push(he(i))}return e}function Dr(s,r,e,t=null,i={}){let a=Or(r,e),o=a[0],d=a[a.length-1],u=new Map;for(let m of a)u.set(m,[]);for(let m of s){let f=m.object?.name||"",b=m.entry_id,w=m.tasks||[];for(let k of w){if(t&&k.responsible_user_id!==t)continue;let q=i[Ht(b,k.id)]??(k.history||[]);for(let N of q){if(typeof N?.timestamp!="string")continue;let B=N.timestamp.slice(0,10);if(Bd)continue;let D=u.get(B);if(!D)continue;let W=N.type??"completed";D.push({date:B,entry_id:b,task_id:k.id,task_name:k.name,object_name:f,status:_a[W]??"ok",days_until_due:null,projected:!1,schedule_type:k.schedule_type,interval_days:k.interval_days??null,responsible_user_id:k.responsible_user_id??null,avg_cost:Pt(N),adaptive_enabled:!!k.adaptive_config?.enabled,prediction_confidence:null,history_timestamp:N.timestamp,history_type:W,history_cost:Pt(N),history_notes:typeof N.notes=="string"?N.notes:null,history_duration:typeof N.duration=="number"?N.duration:null})}}}let _={completed:0,reset:1,skipped:2,triggered:3,trigger_replaced:4};for(let[,m]of u)m.sort((f,b)=>{let w=_[f.history_type??""]??99,k=_[b.history_type??""]??99;if(w!==k)return w-k;let q=f.object_name.localeCompare(b.object_name);return q!==0?q:f.task_name.localeCompare(b.task_name)});return a.map(m=>({date:m,events:u.get(m)??[]}))}var Nr,_a,jt=y(()=>{"use strict";Lt();Pr();Nt();Nr=5;_a={completed:"ok",reset:"ok",skipped:"due_soon",missed:"overdue",triggered:"triggered",trigger_replaced:"triggered",trigger_removed:"ok"}});var Fr,zr=y(()=>{Fr={maintenance:"Maintenance",objects:"Objects",tasks:"Tasks",overdue:"Overdue",due_soon:"Due Soon",triggered:"Triggered",trigger_replaced:"Trigger replaced",trigger_removed:"Trigger removed",ok:"OK",all:"All",new_object:"+ New Object",templates_from:"From template",templates_title:"Start from a template",templates_task_count:"{n} tasks",templates_task_count_one:"1 task",template_created:"Created from template",onboard_hint:"Add your first object to start tracking maintenance.",edit:"Edit",duplicate:"Duplicate",task_duplicated:"Task duplicated",task_moved:"Task moved",move_task_target:"Target object",move_task_message:"Choose the object this task should belong to. Its history, readings and trigger state move with it; it gets a new reference number and its entities are recreated under the new object. Linked documents stay with the current object.",bulk_move_message:"Choose the object the selected tasks should belong to. Tasks that already live there are skipped; history, readings and trigger state move along.",bulk_moved:"{n} tasks moved",more_actions:"More actions",move_task_title:"Move task",move_task:"Move to another object\u2026",object_duplicated:"Object duplicated",delete:"Delete",add_task:"+ Add Task",complete:"Complete",completed:"Completed",skip:"Skip",skipped:"Skipped",missed:"Missed",reset:"Reset",snooze:"Snooze",snoozed:"Snoozed",snoozed_for:"Reminders muted for {hours} hours \u2014 until {until}",snoozed_for_one:"Reminders muted for one hour \u2014 until {until}",cancel:"Cancel",bulk_select:"Select",bulk_select_all:"Select all",bulk_n_selected:"{n} selected",bulk_completed:"{n} tasks completed",bulk_archived:"{n} tasks archived",bulk_delete_objects_confirm:"Delete {n} objects and all their tasks? Archiving keeps their history instead.",bulk_objects_deleted:"{n} objects deleted",bulk_objects_archived:"{n} objects archived",bulk_assign:"Assign",bulk_assign_one:"One person",bulk_assign_rotate:"Several people, in turns",bulk_assign_hint:"Replaces who is assigned to the selected tasks \u2014 a rotation included.",bulk_rotation_min_two:"Pick at least two people.",bulk_labels_add:"Add labels",bulk_labels_remove:"Remove labels",bulk_edit_hint:"Only the ticked settings change; everything else stays as it is.",bulk_no_notifications:"No notifications for these tasks",bulk_updated:"{n} tasks updated",bulk_paused:"{n} tasks paused",bulk_resumed:"{n} tasks resumed",bulk_objects_area:"Area set for {n} objects",bulk_failed:"{n} failed: {reason}",completing:"Completing\u2026",interval:"Interval",warning:"Warning",last_performed:"Last performed",next_due:"Next due",days_until_due:"Days until due",avg_duration:"Avg duration",trigger:"Trigger",trigger_type:"Trigger type",threshold_above:"Upper limit",threshold_below:"Lower limit",threshold:"Threshold",counter:"Counter",state_change:"State change",runtime:"Runtime",trigger_type_due_date:"Date from a sensor",runtime_hours:"Target runtime (hours)",days_before:"Days before the date",days_before_help:`For a sensor that reports when the maintenance is due (a date or timestamp). 0 = on the date itself. The device's own "done" button moves the date forward, which clears the trigger \u2014 make it the completion action.`,target_value:"Target value",target_changes:"Target changes",for_minutes:"For (minutes)",time_based:"Time-based",sensor_based:"Sensor-based",manual:"Manual",one_time:"One-time",weekdays:"Weekdays",nth_weekday:"Nth weekday of month",day_of_month:"Day of month",calendar:"Calendar entity",calendar_entity_label:"Calendar",calendar_entity_hint:"Due once per event of this calendar, on the event's start date \u2014 the next event after the last completion.",calendar_entity_required:"Choose a calendar entity.",recurrence_on_days:"Repeat on",recurrence_occurrence:"Occurrence",recurrence_weekday:"Weekday",recurrence_day:"Day of month (1\u201331)",recurrence_last_day:"Last day of the month",recurrence_business_day:"Business days only (roll back from weekend)",recurrence_offset:"Offset (days, \xB1)",recurrence_offset_help:"Shift the date by \xB1N days, e.g. -2 = two days before.",last_day_month:"Last day of month",last_business_day_month:"Last business day",ord_1:"1st",ord_2:"2nd",ord_3:"3rd",ord_4:"4th",ord_5:"5th",ord_last:"Last",day_word:"Day",interval_value:"Interval",interval_unit:"Unit",unit_days:"Days",unit_weeks:"Weeks",unit_months:"Months",unit_years:"Years",due_date:"Due date",cleaning:"Cleaning",inspection:"Inspection",replacement:"Replacement",calibration:"Calibration",service:"Service",reading:"Reading",custom:"Custom",history:"History",cost:"Cost",cost_kind_credit:"Credit",cost_credit_hint:"Money back \u2014 a sale, a refund. It lowers the totals; the average per completion leaves it out.",report_button:"Report",report_title:"Maintenance report",report_generated:"Generated",report_times_done:"Done",report_total_cost:"Total cost",report_notes:"Notes",report_col_type:"Type",report_col_status:"Status",report_col_schedule:"Schedule",duration:"Duration",minutes_short:"min",both:"Both",trigger_val:"Trigger value",complete_title:"Complete: ",checklist:"Checklist",require_on_completion:"Require on completion",checklist_steps_optional:"Checklist steps (optional)",checklist_placeholder:`Clean filter +/*! maintenance_supporter frontend 2.98.0 */ +var fr=Object.defineProperty;var _a=Object.getOwnPropertyDescriptor;var y=(s,r,e)=>()=>{if(e)throw e[0];try{return s&&(r=s(s=0)),r}catch(t){throw e=[t],t}};var ma=(s,r)=>{for(var e in r)fr(s,e,{get:r[e],enumerable:!0})};var c=(s,r,e,t)=>{for(var i=t>1?void 0:t?_a(r,e):r,a=s.length-1,o;a>=0;a--)(o=s[a])&&(i=(t?o(r,e,i):o(i))||i);return t&&i&&fr(r,e,i),i};var Rt,Pt,mi,vr,ct,yr,E,br,gi,fi=y(()=>{Rt=globalThis,Pt=Rt.ShadowRoot&&(Rt.ShadyCSS===void 0||Rt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,mi=Symbol(),vr=new WeakMap,ct=class{constructor(r,e,t){if(this._$cssResult$=!0,t!==mi)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=r,this.t=e}get styleSheet(){let r=this.o,e=this.t;if(Pt&&r===void 0){let t=e!==void 0&&e.length===1;t&&(r=vr.get(e)),r===void 0&&((this.o=r=new CSSStyleSheet).replaceSync(this.cssText),t&&vr.set(e,r))}return r}toString(){return this.cssText}},yr=s=>new ct(typeof s=="string"?s:s+"",void 0,mi),E=(s,...r)=>{let e=s.length===1?s[0]:r.reduce((t,i,a)=>t+(o=>{if(o._$cssResult$===!0)return o.cssText;if(typeof o=="number")return o;throw Error("Value passed to 'css' function must be a 'css' function result: "+o+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+s[a+1],s[0]);return new ct(e,s,mi)},br=(s,r)=>{if(Pt)s.adoptedStyleSheets=r.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let e of r){let t=document.createElement("style"),i=Rt.litNonce;i!==void 0&&t.setAttribute("nonce",i),t.textContent=e.cssText,s.appendChild(t)}},gi=Pt?s=>s:s=>s instanceof CSSStyleSheet?(r=>{let e="";for(let t of r.cssRules)e+=t.cssText;return yr(e)})(s):s});var ga,fa,va,ya,ba,xa,Lt,xr,wa,ka,dt,pt,Nt,wr,ce,ut=y(()=>{fi();fi();({is:ga,defineProperty:fa,getOwnPropertyDescriptor:va,getOwnPropertyNames:ya,getOwnPropertySymbols:ba,getPrototypeOf:xa}=Object),Lt=globalThis,xr=Lt.trustedTypes,wa=xr?xr.emptyScript:"",ka=Lt.reactiveElementPolyfillSupport,dt=(s,r)=>s,pt={toAttribute(s,r){switch(r){case Boolean:s=s?wa:null;break;case Object:case Array:s=s==null?s:JSON.stringify(s)}return s},fromAttribute(s,r){let e=s;switch(r){case Boolean:e=s!==null;break;case Number:e=s===null?null:Number(s);break;case Object:case Array:try{e=JSON.parse(s)}catch{e=null}}return e}},Nt=(s,r)=>!ga(s,r),wr={attribute:!0,type:String,converter:pt,reflect:!1,useDefault:!1,hasChanged:Nt};Symbol.metadata??=Symbol("metadata"),Lt.litPropertyMetadata??=new WeakMap;ce=class extends HTMLElement{static addInitializer(r){this._$Ei(),(this.l??=[]).push(r)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(r,e=wr){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(r)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(r,e),!e.noAccessor){let t=Symbol(),i=this.getPropertyDescriptor(r,t,e);i!==void 0&&fa(this.prototype,r,i)}}static getPropertyDescriptor(r,e,t){let{get:i,set:a}=va(this.prototype,r)??{get(){return this[e]},set(o){this[e]=o}};return{get:i,set(o){let d=i?.call(this);a?.call(this,o),this.requestUpdate(r,d,t)},configurable:!0,enumerable:!0}}static getPropertyOptions(r){return this.elementProperties.get(r)??wr}static _$Ei(){if(this.hasOwnProperty(dt("elementProperties")))return;let r=xa(this);r.finalize(),r.l!==void 0&&(this.l=[...r.l]),this.elementProperties=new Map(r.elementProperties)}static finalize(){if(this.hasOwnProperty(dt("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(dt("properties"))){let e=this.properties,t=[...ya(e),...ba(e)];for(let i of t)this.createProperty(i,e[i])}let r=this[Symbol.metadata];if(r!==null){let e=litPropertyMetadata.get(r);if(e!==void 0)for(let[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let i=this._$Eu(e,t);i!==void 0&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(r){let e=[];if(Array.isArray(r)){let t=new Set(r.flat(1/0).reverse());for(let i of t)e.unshift(gi(i))}else r!==void 0&&e.push(gi(r));return e}static _$Eu(r,e){let t=e.attribute;return t===!1?void 0:typeof t=="string"?t:typeof r=="string"?r.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(r=>this.enableUpdating=r),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(r=>r(this))}addController(r){(this._$EO??=new Set).add(r),this.renderRoot!==void 0&&this.isConnected&&r.hostConnected?.()}removeController(r){this._$EO?.delete(r)}_$E_(){let r=new Map,e=this.constructor.elementProperties;for(let t of e.keys())this.hasOwnProperty(t)&&(r.set(t,this[t]),delete this[t]);r.size>0&&(this._$Ep=r)}createRenderRoot(){let r=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return br(r,this.constructor.elementStyles),r}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(r=>r.hostConnected?.())}enableUpdating(r){}disconnectedCallback(){this._$EO?.forEach(r=>r.hostDisconnected?.())}attributeChangedCallback(r,e,t){this._$AK(r,t)}_$ET(r,e){let t=this.constructor.elementProperties.get(r),i=this.constructor._$Eu(r,t);if(i!==void 0&&t.reflect===!0){let a=(t.converter?.toAttribute!==void 0?t.converter:pt).toAttribute(e,t.type);this._$Em=r,a==null?this.removeAttribute(i):this.setAttribute(i,a),this._$Em=null}}_$AK(r,e){let t=this.constructor,i=t._$Eh.get(r);if(i!==void 0&&this._$Em!==i){let a=t.getPropertyOptions(i),o=typeof a.converter=="function"?{fromAttribute:a.converter}:a.converter?.fromAttribute!==void 0?a.converter:pt;this._$Em=i;let d=o.fromAttribute(e,a.type);this[i]=d??this._$Ej?.get(i)??d,this._$Em=null}}requestUpdate(r,e,t,i=!1,a){if(r!==void 0){let o=this.constructor;if(i===!1&&(a=this[r]),t??=o.getPropertyOptions(r),!((t.hasChanged??Nt)(a,e)||t.useDefault&&t.reflect&&a===this._$Ej?.get(r)&&!this.hasAttribute(o._$Eu(r,t))))return;this.C(r,e,t)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(r,e,{useDefault:t,reflect:i,wrapped:a},o){t&&!(this._$Ej??=new Map).has(r)&&(this._$Ej.set(r,o??e??this[r]),a!==!0||o!==void 0)||(this._$AL.has(r)||(this.hasUpdated||t||(e=void 0),this._$AL.set(r,e)),i===!0&&this._$Em!==r&&(this._$Eq??=new Set).add(r))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}let r=this.scheduleUpdate();return r!=null&&await r,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(let[i,a]of this._$Ep)this[i]=a;this._$Ep=void 0}let t=this.constructor.elementProperties;if(t.size>0)for(let[i,a]of t){let{wrapped:o}=a,d=this[i];o!==!0||this._$AL.has(i)||d===void 0||this.C(i,void 0,a,d)}}let r=!1,e=this._$AL;try{r=this.shouldUpdate(e),r?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(t){throw r=!1,this._$EM(),t}r&&this._$AE(e)}willUpdate(r){}_$AE(r){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(r)),this.updated(r)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(r){return!0}update(r){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(r){}firstUpdated(r){}};ce.elementStyles=[],ce.shadowRootOptions={mode:"open"},ce[dt("elementProperties")]=new Map,ce[dt("finalized")]=new Map,ka?.({ReactiveElement:ce}),(Lt.reactiveElementVersions??=[]).push("2.1.2")});function Lr(s,r){if(!Ei(s)||!s.hasOwnProperty("raw"))throw Error("invalid template strings array");return $r!==void 0?$r.createHTML(r):r}function Ue(s,r,e=s,t){if(r===Ce)return r;let i=t!==void 0?e._$Co?.[t]:e._$Cl,a=mt(r)?void 0:r._$litDirective$;return i?.constructor!==a&&(i?._$AO?.(!1),a===void 0?i=void 0:(i=new a(s),i._$AT(s,e,t)),t!==void 0?(e._$Co??=[])[t]=i:e._$Cl=i),i!==void 0&&(r=Ue(s,i._$AS(s,r.values),i,t)),r}var $i,kr,qt,$r,Ir,me,Rr,$a,Ae,_t,mt,Ei,Ea,vi,ht,Er,Sr,Se,Tr,Ar,Pr,Si,l,We,bl,Ce,h,Cr,Te,Sa,gt,yi,ft,Be,bi,xi,wi,ki,Ta,Nr,Ht=y(()=>{$i=globalThis,kr=s=>s,qt=$i.trustedTypes,$r=qt?qt.createPolicy("lit-html",{createHTML:s=>s}):void 0,Ir="$lit$",me=`lit$${Math.random().toFixed(9).slice(2)}$`,Rr="?"+me,$a=`<${Rr}>`,Ae=document,_t=()=>Ae.createComment(""),mt=s=>s===null||typeof s!="object"&&typeof s!="function",Ei=Array.isArray,Ea=s=>Ei(s)||typeof s?.[Symbol.iterator]=="function",vi=`[ +\f\r]`,ht=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Er=/-->/g,Sr=/>/g,Se=RegExp(`>|${vi}(?:([^\\s"'>=/]+)(${vi}*=${vi}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),Tr=/'/g,Ar=/"/g,Pr=/^(?:script|style|textarea|title)$/i,Si=s=>(r,...e)=>({_$litType$:s,strings:r,values:e}),l=Si(1),We=Si(2),bl=Si(3),Ce=Symbol.for("lit-noChange"),h=Symbol.for("lit-nothing"),Cr=new WeakMap,Te=Ae.createTreeWalker(Ae,129);Sa=(s,r)=>{let e=s.length-1,t=[],i,a=r===2?"":r===3?"":"",o=ht;for(let d=0;d"?(o=i??ht,f=-1):m[1]===void 0?f=-2:(f=o.lastIndex-m[2].length,_=m[1],o=m[3]===void 0?Se:m[3]==='"'?Ar:Tr):o===Ar||o===Tr?o=Se:o===Er||o===Sr?o=ht:(o=Se,i=void 0);let w=o===Se&&s[d+1].startsWith("/>")?" ":"";a+=o===ht?p+$a:f>=0?(t.push(_),p.slice(0,f)+Ir+p.slice(f)+me+w):p+me+(f===-2?d:w)}return[Lr(s,a+(s[e]||"")+(r===2?"":r===3?"":"")),t]},gt=class s{constructor({strings:r,_$litType$:e},t){let i;this.parts=[];let a=0,o=0,d=r.length-1,p=this.parts,[_,m]=Sa(r,e);if(this.el=s.createElement(_,t),Te.currentNode=this.el.content,e===2||e===3){let f=this.el.content.firstChild;f.replaceWith(...f.childNodes)}for(;(i=Te.nextNode())!==null&&p.length0){i.textContent=qt?qt.emptyScript:"";for(let w=0;w2||t[0]!==""||t[1]!==""?(this._$AH=Array(t.length-1).fill(new String),this.strings=t):this._$AH=h}_$AI(r,e=this,t,i){let a=this.strings,o=!1;if(a===void 0)r=Ue(this,r,e,0),o=!mt(r)||r!==this._$AH&&r!==Ce,o&&(this._$AH=r);else{let d=r,p,_;for(r=a[0],p=0;p{let t=e?.renderBefore??r,i=t._$litPart$;if(i===void 0){let a=e?.renderBefore??null;t._$litPart$=i=new ft(r.insertBefore(_t(),a),a,void 0,e??{})}return i._$AI(s),i}});var Ti,S,Aa,qr=y(()=>{ut();ut();Ht();Ht();Ti=globalThis,S=class extends ce{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){let r=super.createRenderRoot();return this.renderOptions.renderBefore??=r.firstChild,r}update(r){let e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(r),this._$Do=Nr(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return Ce}};S._$litElement$=!0,S.finalized=!0,Ti.litElementHydrateSupport?.({LitElement:S});Aa=Ti.litElementPolyfillSupport;Aa?.({LitElement:S});(Ti.litElementVersions??=[]).push("4.2.2")});var Hr=y(()=>{});var I=y(()=>{ut();Ht();qr();Hr()});var Or=y(()=>{});function v(s){return(r,e)=>typeof e=="object"?Ia(s,r,e):((t,i,a)=>{let o=i.hasOwnProperty(a);return i.constructor.createProperty(a,t),o?Object.getOwnPropertyDescriptor(i,a):void 0})(s,r,e)}var Ca,Ia,Ai=y(()=>{ut();Ca={attribute:!0,type:String,converter:pt,reflect:!1,hasChanged:Nt},Ia=(s=Ca,r,e)=>{let{kind:t,metadata:i}=e,a=globalThis.litPropertyMetadata.get(i);if(a===void 0&&globalThis.litPropertyMetadata.set(i,a=new Map),t==="setter"&&((s=Object.create(s)).wrapped=!0),a.set(e.name,s),t==="accessor"){let{name:o}=e;return{set(d){let p=r.get.call(this);r.set.call(this,d),this.requestUpdate(o,p,s,!0,d)},init(d){return d!==void 0&&this.C(o,void 0,s,d),d}}}if(t==="setter"){let{name:o}=e;return function(d){let p=this[o];r.call(this,d),this.requestUpdate(o,p,s,!0,d)}}throw Error("Unsupported decorator location: "+t)}});function u(s){return v({...s,state:!0,attribute:!1})}var jr=y(()=>{Ai();});var Dr=y(()=>{});var Ve=y(()=>{});var Mr=y(()=>{Ve();});var zr=y(()=>{Ve();});var Fr=y(()=>{Ve();});var Ur=y(()=>{Ve();});var Br=y(()=>{Ve();});var F=y(()=>{Or();Ai();jr();Dr();Mr();zr();Fr();Ur();Br()});function Wr(s){return s==="use"?"use":"purchase"}function Vr(s){let r=jt(s?.cost);if(r===null)return null;let e=jt(s?.package_size);return e&&e>0?r/e:r}function Dt(s){let r=jt(s.cost);if(s.cost_basis!=="use")return r;let e=jt(s.parts_cost),t=s.purchase?null:r;return t===null&&e===null?null:(t??0)+(e??0)}var jt,Mt=y(()=>{"use strict";jt=s=>typeof s=="number"&&Number.isFinite(s)?s:null});function Ii(s){return Pa[s??""]??Kr.length}var Ci,Kr,Pa,vt,zt=y(()=>{"use strict";Ci={ok:"var(--success-color, #4caf50)",due_soon:"var(--warning-color, #ff9800)",overdue:"var(--error-color, #f44336)",triggered:"var(--deep-orange-color, #ff5722)",archived:"var(--disabled-color, #9e9e9e)",paused:"var(--info-color, #2196f3)"},Kr=["overdue","triggered","due_soon","ok"],Pa=Object.fromEntries(Kr.map((s,r)=>[s,r]));vt={ok:"mdi:check-circle",due_soon:"mdi:alert-circle",overdue:"mdi:alert-octagon",triggered:"mdi:bell-alert",archived:"mdi:archive-outline",paused:"mdi:pause-circle-outline",completed:"mdi:check-circle",skipped:"mdi:skip-next",missed:"mdi:calendar-remove",reset:"mdi:refresh"}});function yt(s){let r=Qr.get(s);if(r===void 0){try{r=new Intl.DateTimeFormat("en-US",{timeZone:s,hourCycle:"h23",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"})}catch{r=null}Qr.set(s,r)}return r}function Jr(){if(Ri===void 0){let s;try{s=new Intl.DateTimeFormat().resolvedOptions().timeZone}catch{}Ri=s&&yt(s)?s:null}return Ri}function Xr(){return Jr()??"UTC"}function ge(){let s=Zr.serverTimeZone;return s&&yt(s)?s:Xr()}function fe(){let s=Jr();return Zr.timeZone!=="server"&&s?s:ge()}function Ft(s,r){let e=yt(r)??yt(Xr())??yt("UTC"),t={};for(let i of e.formatToParts(s))i.type!=="literal"&&(t[i.type]=Number(i.value));return{y:t.year,mo:t.month,d:t.day,h:t.hour%24,mi:t.minute,s:t.second}}function ve(s,r){return Li(Ft(+s,r))}function Ut(s,r){let e=Ft(+s,r);return`${Li(e)}T${te(e.h)}:${te(e.mi)}`}function Ie(){return ve(Date.now(),ge())}function es(){return Ut(Date.now(),ge())}function Pi(s,r){let e=Ft(s,r);return Date.UTC(e.y,e.mo-1,e.d,e.h,e.mi,e.s)-Math.floor(s/1e3)*1e3}function La(s,r,e,t,i,a,o){let d=Date.UTC(s,r-1,e,t,i,a),p=Pi(d-Yr,o),_=Pi(d+Yr,o);for(let m of p===_?[p]:[p,_])if(Pi(d-m,o)===m)return d-m;return d-p}function ts(s,r,e){if(s.has(r))return s.get(r);s.size>=Ha&&s.clear();let t=e();return s.set(r,t),t}function Q(s){if(typeof s!="string"||!s)return NaN;let r=Ni.exec(s);if(!r)return Date.parse(s);let e=ge();return ts(qa,`${e}|${s}`,()=>{let[t,i,a,o,d,p]=[r[1],r[2],r[3],r[4]??"0",r[5]??"0",r[6]??"0"].map(Number);if(i<1||i>12||new Date(Date.UTC(t,i-1,a)).getUTCDate()!==a||o>23||d>59||p>59)return NaN;let _=r[7]?Math.floor(+`0.${r[7]}`*1e3):0;return La(t,i,a,o,d,p,e)+_})}function qi(s){if(typeof s!="string"||!s)return null;let r=Ni.exec(s);if(r)return`${r[1]}-${r[2]}-${r[3]}`;let e=ge();return ts(Na,`${e}|${s}`,()=>{let t=Date.parse(s);return Number.isFinite(t)?ve(t,e):/^\d{4}-\d{2}-\d{2}/.test(s)?s.slice(0,10):null})}function is(s){if(typeof s!="string"||!s)return"";let r=Ni.exec(s);if(r)return`${r[1]}-${r[2]}-${r[3]}T${r[4]??"00"}:${r[5]??"00"}:${r[6]??"00"}`;let e=Date.parse(s);if(!Number.isFinite(e))return"";let t=Ft(e,ge());return`${Li(t)}T${te(t.h)}:${te(t.mi)}:${te(t.s)}`}function Re(s,r){let e=Oa.exec(s);if(!e)return s;let t=new Date(Date.UTC(Number(e[1]),Number(e[2])-1,Number(e[3])+Math.round(r)));return`${te(t.getUTCFullYear(),4)}-${te(t.getUTCMonth()+1)}-${te(t.getUTCDate())}`}var Zr,Yr,te,Qr,Ri,Li,Ni,Na,qa,Ha,Oa,Z=y(()=>{"use strict";Zr=window.__msDateTimePrefs??={},Yr=864e5,te=(s,r=2)=>String(s).padStart(r,"0"),Qr=new Map;Li=s=>`${te(s.y,4)}-${te(s.mo)}-${te(s.d)}`;Ni=/^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d+))?)?)?$/,Na=new Map,qa=new Map,Ha=2e4;Oa=/^(\d{4})-(\d{2})-(\d{2})$/});var ds,cs=y(()=>{ds={maintenance:"Maintenance",objects:"Objects",tasks:"Tasks",overdue:"Overdue",due_soon:"Due Soon",triggered:"Triggered",trigger_replaced:"Trigger replaced",trigger_removed:"Trigger removed",ok:"OK",all:"All",new_object:"+ New Object",templates_from:"From template",templates_title:"Start from a template",templates_task_count:"{n} tasks",templates_task_count_one:"1 task",template_created:"Created from template",onboard_hint:"Add your first object to start tracking maintenance.",edit:"Edit",duplicate:"Duplicate",task_duplicated:"Task duplicated",task_moved:"Task moved",move_task_target:"Target object",move_task_message:"Choose the object this task should belong to. Its history, readings and trigger state move with it; it gets a new reference number and its entities are recreated under the new object. Linked documents stay with the current object.",bulk_move_message:"Choose the object the selected tasks should belong to. Tasks that already live there are skipped; history, readings and trigger state move along.",bulk_moved:"{n} tasks moved",more_actions:"More actions",move_task_title:"Move task",move_task:"Move to another object\u2026",object_duplicated:"Object duplicated",delete:"Delete",add_task:"+ Add Task",complete:"Complete",completed:"Completed",skip:"Skip",skipped:"Skipped",missed:"Missed",reset:"Reset",snooze:"Snooze",snoozed:"Snoozed",snoozed_for:"Reminders muted for {hours} hours \u2014 until {until}",snoozed_for_one:"Reminders muted for one hour \u2014 until {until}",cancel:"Cancel",bulk_select:"Select",bulk_select_all:"Select all",bulk_n_selected:"{n} selected",bulk_completed:"{n} tasks completed",bulk_archived:"{n} tasks archived",bulk_delete_objects_confirm:"Delete {n} objects and all their tasks? Archiving keeps their history instead.",bulk_objects_deleted:"{n} objects deleted",bulk_objects_archived:"{n} objects archived",bulk_assign:"Assign",bulk_assign_one:"One person",bulk_assign_rotate:"Several people, in turns",bulk_assign_hint:"Replaces who is assigned to the selected tasks \u2014 a rotation included.",bulk_rotation_min_two:"Pick at least two people.",bulk_labels_add:"Add labels",bulk_labels_remove:"Remove labels",bulk_edit_hint:"Only the ticked settings change; everything else stays as it is.",bulk_no_notifications:"No notifications for these tasks",bulk_updated:"{n} tasks updated",bulk_paused:"{n} tasks paused",bulk_resumed:"{n} tasks resumed",bulk_objects_area:"Area set for {n} objects",bulk_failed:"{n} failed: {reason}",completing:"Completing\u2026",interval:"Interval",warning:"Warning",last_performed:"Last performed",next_due:"Next due",days_until_due:"Days until due",avg_duration:"Avg duration",trigger:"Trigger",trigger_type:"Trigger type",threshold_above:"Upper limit",threshold_below:"Lower limit",threshold:"Threshold",counter:"Counter",state_change:"State change",runtime:"Runtime",trigger_type_due_date:"Date from a sensor",runtime_hours:"Target runtime (hours)",days_before:"Days before the date",days_before_help:`For a sensor that reports when the maintenance is due (a date or timestamp). 0 = on the date itself. The device's own "done" button moves the date forward, which clears the trigger \u2014 make it the completion action.`,target_value:"Target value",target_changes:"Target changes",for_minutes:"For (minutes)",time_based:"Time-based",sensor_based:"Sensor-based",manual:"Manual",one_time:"One-time",weekdays:"Weekdays",nth_weekday:"Nth weekday of month",day_of_month:"Day of month",calendar:"Calendar entity",calendar_entity_label:"Calendar",calendar_entity_hint:"Due once per event of this calendar, on the event's start date \u2014 the next event after the last completion.",calendar_entity_required:"Choose a calendar entity.",recurrence_on_days:"Repeat on",recurrence_occurrence:"Occurrence",recurrence_weekday:"Weekday",recurrence_day:"Day of month (1\u201331)",recurrence_last_day:"Last day of the month",recurrence_business_day:"Business days only (roll back from weekend)",recurrence_offset:"Offset (days, \xB1)",recurrence_offset_help:"Shift the date by \xB1N days, e.g. -2 = two days before.",last_day_month:"Last day of month",last_business_day_month:"Last business day",ord_1:"1st",ord_2:"2nd",ord_3:"3rd",ord_4:"4th",ord_5:"5th",ord_last:"Last",day_word:"Day",interval_value:"Interval",interval_unit:"Unit",unit_days:"Days",unit_weeks:"Weeks",unit_months:"Months",unit_years:"Years",due_date:"Due date",cleaning:"Cleaning",inspection:"Inspection",replacement:"Replacement",calibration:"Calibration",service:"Service",reading:"Reading",custom:"Custom",history:"History",cost:"Cost",cost_kind_credit:"Credit",cost_credit_hint:"Money back \u2014 a sale, a refund. It lowers the totals; the average per completion leaves it out.",report_button:"Report",report_title:"Maintenance report",report_generated:"Generated",report_times_done:"Done",report_total_cost:"Total cost",report_notes:"Notes",report_col_type:"Type",report_col_status:"Status",report_col_schedule:"Schedule",duration:"Duration",minutes_short:"min",both:"Both",trigger_val:"Trigger value",complete_title:"Complete: ",checklist:"Checklist",require_on_completion:"Require on completion",checklist_steps_optional:"Checklist steps (optional)",checklist_placeholder:`Clean filter Replace seal -Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"{field}: too long (max {n} characters)",err_too_short:"{field}: too short (min {n} characters)",err_value_too_high:"{field}: too large (max {n})",err_value_too_low:"{field}: too small (min {n})",err_required:"{field}: required",err_wrong_type:"{field}: wrong type (expected: {type})",err_invalid_choice:"{field}: not an allowed value",err_invalid_value:"{field}: invalid value",ws_err_not_found:"Not found \u2014 it may have been deleted meanwhile",ws_err_invalid_input:"Invalid input",ws_err_invalid_date:"Invalid date",ws_err_invalid_format:"Invalid format",ws_err_invalid_url:"Invalid link",ws_err_invalid_mirror_todo:"Invalid to-do list",ws_err_not_configured:"Not set up yet",ws_err_not_loaded:"Not loaded yet \u2014 try again in a moment",ws_err_limit_reached:"Limit reached",ws_err_too_many:"Too many entries",ws_err_too_large:"Too large",ws_err_not_archived:"Not archived",ws_err_invalid_target:"Invalid target",ws_err_create_failed:"Could not be created",ws_err_archived:"Archived \u2014 restore it first",ws_err_already_archived:"Already archived",ws_err_unavailable:"Currently unavailable",ws_err_unauthorized:"Not permitted for your user",ws_err_too_many_views:"Too many saved views",ws_err_storage_unavailable:"Storage is not available",ws_err_replace_failed:"Replacement failed",ws_err_not_paused:"Not paused",ws_err_not_available:"Nothing available",ws_err_no_url:"No link given",ws_err_no_phases:"This task has no phases",ws_err_invalid_view:"Invalid view",ws_err_invalid_user:"Unknown user",ws_err_invalid_range:"Invalid range",ws_err_invalid_parent:"Invalid parent object",ws_err_invalid_device:"Unknown device",ws_err_empty_csv:"The CSV file is empty",ws_err_empty:"Nothing to import",ws_err_duplicate_failed:"Could not duplicate",ws_err_already_paused:"Already paused",ws_err_invalid_cursor:"Invalid phase position",ws_err_invalid_entity_slug:"Invalid entity name",ws_err_invalid_trigger_config:"Invalid trigger configuration",ws_err_no_defaults:"No quick-complete defaults set",ws_err_self_link_device:"An object cannot be linked to itself",ws_err_too_early:"Too early \u2014 this task can only be completed closer to its due date",ws_err_invalid_search_template:"The search link must be an http(s) URL containing {q}",ws_err_invalid_icon:"The notification icon must be an mdi: name, e.g. mdi:air-filter",feat_schedule_time:"Time-of-day scheduling",feat_schedule_time_desc:"Tasks become overdue at a specific time of day instead of midnight.",schedule_time_toggle:"Due at a specific time",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",notes_markdown_hint:"Markdown is supported \u2014 **bold**, lists, [links](\u2026)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",completed_at_optional:"Completed at (optional, empty = now)",completed_at_pick:"Set date & time",completed_at_future_error:"The completion date cannot be in the future.",days:"days",day:"day",today:"Today",d_overdue:"d overdue",no_tasks:"No maintenance tasks yet. Create an object to get started.",no_tasks_short:"No tasks",no_history:"No history entries yet.",show_all:"Show all",cost_duration_chart:"Cost & Duration",installed:"Installed",min:"Min",max:"Max",save:"Save",saving:"Saving\u2026",edit_task:"Edit Task",new_task:"New Maintenance Task",task_name:"Task name",maintenance_type:"Maintenance type",priority:"Priority",labels:"Labels",labels_placeholder:"e.g. safety, seasonal, tenant-visible",labels_help:"Comma-separated tags for filtering and reporting.",task_mirror_todo:"Mirror into to-do lists",task_mirror_todo_hint:"The task appears in these lists while it is due; checking it off there completes it here.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",all_priorities:"All priorities",schedule_type:"Schedule type",interval_days:"Interval (days)",warning_days:"Warning days",earliest_completion_days:"Earliest completion (days before due)",earliest_completion_days_help:"Leave empty to allow completing any time. 0 = only on/after the due date.",last_performed_optional:"Last performed (optional)",interval_anchor:"Interval anchor",anchor_completion:"From completion date",anchor_planned:"From planned date (no drift)",edit_object:"Edit Object",name:"Name",manufacturer_optional:"Manufacturer (optional)",model_optional:"Model (optional)",serial_number_optional:"Serial number (optional)",serial_number_label:"S/N",object_replaces:"Replaces",object_replaced_by:"Replaced by",documentation_url_label:"Manual",object_notes_label:"Notes",sort_due_date:"Due date",sort_object:"Object name",sort_type:"Type",sort_task_name:"Task name",all_objects:"All objects",all_parts:"All parts",all_areas:"All areas",areas_filter:"Filter areas\u2026",areas_filter_none:"No area matches the filter.",area_cost_year:"Cost {year}",area_object_count:"{n} objects",area_object_count_one:"1 object",area_range_12m:"Last 12 months",area_range_all:"All time",area_task_filter:"Filter by task\u2026",area_kpi_completions:"Completions",area_kpi_total_time:"Total time",area_cost_per_month:"Cost per month",area_cost_per_year:"Cost per year",area_cost_per_object:"Cost per object",area_cost_share:"Share",area_history_section:"History (all objects)",area_report_print:"Area report (PDF)",area_report_title:"Area report",area_report_period:"Period",area_no_costs:"No costs recorded in this period.",no_tasks_yet:"No tasks yet",add_first_task:"Add first task",trigger_configuration:"Trigger Configuration",entity_id:"Entity ID",comma_separated:"comma-separated",entity_logic:"Entity logic",entity_logic_any:"Any entity triggers",entity_logic_all:"All entities must trigger",entities:"entities",attribute_optional:"Attribute (optional, blank = state)",use_entity_state:"Use entity state (no attribute)",trigger_above:"Trigger above",trigger_below:"Trigger below",trigger_equals:"Trigger when equal to (=)",trigger_not_equals:"Trigger when different from (\u2260)",for_at_least_minutes:"For at least (minutes)",safety_interval:"Safety interval (optional)",trigger_combinator:"Combine trigger and interval",trigger_combinator_any:"Trigger or interval (whichever first)",trigger_combinator_all:"Trigger and interval (both required)",delta_mode:"Delta mode",from_state_optional:"From state (optional)",to_state_optional:"To state (optional)",documentation_url_optional:"Documentation URL (optional)",object_notes_optional:"Notes (optional)",nfc_tag_id_optional:"NFC Tag ID (optional)",nfc_tags_empty_help:"No NFC tags registered in Home Assistant yet.",nfc_tags_open_settings:"Open Tags settings",nfc_tags_refresh:"Refresh",environmental_entity_optional:"Environmental sensor (optional)",environmental_entity_helper:"e.g. sensor.outdoor_temperature \u2014 adjusts the interval based on environmental conditions",adaptive_prediction_enabled:"Enable sensor-driven predictions",adaptive_seasonal_enabled:"Enable seasonal awareness",adaptive_max_interval:"Maximum interval (days)",adaptive_min_interval:"Minimum interval (days)",adaptive_ewa_alpha:"Learning rate (alpha)",adaptive_enabled:"Enable adaptive scheduling",adaptive_section_title:"Adaptive Scheduling",environmental_attribute_optional:"Environmental attribute (optional)",nfc_tag_id:"NFC Tag ID",nfc_linked:"NFC tag linked",nfc_link_hint:"Click to link NFC tag",responsible_user:"Responsible User",shared_with:"Shared with (rotation)",shared_with_help:"Pick multiple people to share this task; the responsible person rotates on each completion.",rotation_strategy:"Rotation",rotation_none:"No rotation",rotation_round_robin:"Round-robin",rotation_least_completed:"Least completed",rotation_random:"Random",no_user_assigned:"(No user assigned)",all_users:"All Users",my_tasks:"My Tasks",tab_calendar:"Calendar",cal_no_events:"No maintenance",cal_every_n_days:"every {n} days",cal_every_day:"every day",cal_source_time:"Time-based",cal_source_time_adaptive:"Time-based (adaptive)",cal_source_sensor:"Sensor-based",cal_predicted:"predicted",cal_confidence_high:"high confidence",cal_confidence_medium:"medium confidence",cal_confidence_low:"low confidence",budget_monthly:"Monthly budget",budget_yearly:"Yearly budget",groups:"Groups",new_group:"New group",edit_group:"Edit group",no_groups:"No groups yet",delete_group:"Delete group",delete_group_confirm:"Delete group '{name}'?",group_select_tasks:"Select tasks",group_name_required:"Name is required",description_optional:"Description (optional)",selected:"Selected",loading_chart:"Loading chart data...",hide_outliers:"Hide outliers (sensor glitches)",was_maintenance_needed:"Was this maintenance needed?",feedback_needed:"Needed",feedback_not_needed:"Not needed",feedback_not_sure:"Not sure",suggested_interval:"Suggested interval",apply_suggestion:"Apply",reanalyze:"Re-analyze",reanalyze_result:"New analysis",reanalyze_insufficient_data:"Not enough data to produce a recommendation",data_points:"data points",dismiss_suggestion:"Dismiss",confidence_low:"Low",confidence_medium:"Medium",confidence_high:"High",recommended:"recommended",seasonal_awareness:"Seasonal Awareness",edit_seasonal_overrides:"Edit seasonal factors",seasonal_overrides_title:"Seasonal factors (override)",seasonal_overrides_hint:"Factor per month (0.1\u20135.0). Empty = learned automatically.",seasonal_override_invalid:"Invalid value",seasonal_override_range:"Factor must be between 0.1 and 5.0",clear_all:"Clear all",clear:"Clear",date_type_toggle:"Type the date",date_type_invalid:"Not a valid date \u2014 try 1978-03-15, a year like 1978, or your own date format",seasonal_chart_title:"Seasonal Factors",seasonal_learned:"Learned",seasonal_manual:"Manual",month_jan:"Jan",month_feb:"Feb",month_mar:"Mar",month_apr:"Apr",month_may:"May",month_jun:"Jun",month_jul:"Jul",month_aug:"Aug",month_sep:"Sep",month_oct:"Oct",month_nov:"Nov",month_dec:"Dec",sensor_prediction:"Sensor Prediction",degradation_trend:"Trend",trend_rising:"Rising",trend_falling:"Falling",trend_stable:"Stable",trend_insufficient_data:"Insufficient data",days_until_threshold:"Days until threshold",threshold_exceeded:"Threshold exceeded",environmental_adjustment:"Environmental factor",sensor_prediction_urgency:"Sensor predicts threshold in ~{days} days",day_short:"day",weibull_reliability_curve:"Reliability Curve",weibull_failure_probability:"Failure Probability",weibull_r_squared:"Fit R\xB2",beta_early_failures:"Early Failures",beta_random_failures:"Random Failures",beta_wear_out:"Wear-out",beta_highly_predictable:"Highly Predictable",confidence_interval:"Confidence Interval",confidence_conservative:"Conservative",confidence_aggressive:"Optimistic",current_interval_marker:"Current interval",recommended_marker:"Recommended",characteristic_life:"Characteristic life",chart_mini_sparkline:"Trend sparkline",chart_history:"Cost and duration history",chart_seasonal:"Seasonal factors, 12 months",chart_weibull:"Weibull reliability curve",chart_sparkline:"Sensor trigger value chart",days_progress:"Days progress",qr_code:"QR Code",qr_generating:"Generating QR code\u2026",qr_error:"Failed to generate QR code.",qr_error_no_url:"No HA URL configured. Please set an external or internal URL in Settings \u2192 System \u2192 Network.",save_error:"Failed to save. Please try again.",subsave_warning:"Saved, but one setting was rejected: {detail}",qr_print:"Print",qr_download:"Download SVG",qr_action_view:"View maintenance info",qr_action_complete:"Mark maintenance as complete",qr_action_quick_complete:"Quick-complete \u2014 no dialog",qr_print_quick_none:"Quick-complete codes need quick-complete defaults on a task (Settings \u2192 Advanced Features \u2192 Completion actions; task dialog \u2192 Quick-complete defaults) \u2014 none of the chosen objects' tasks has them yet.",qr_url_mode:"Link type",qr_mode_companion:"Companion App",qr_mode_local:"Local (mDNS)",qr_mode_server:"Server URL",overview:"Overview",strat_this_month:"This Month",strat_later:"Later",strat_week:"Week",strat_fortnight:"Fortnight",strat_month:"Month",strat_year:"Year",strat_other:"Other",strat_status:"Status",strat_empty_title:"No maintenance objects yet",strat_empty_content:"Open the Maintenance panel to add your first object \u2014 pool pump, HVAC filter, vehicle, anything that needs scheduled care.",strat_open_panel:"Open Maintenance panel",strat_add_object:"Add object",strat_not_loaded:"**Maintenance Supporter** is not loaded. Install/enable the integration first.",strat_group_by:"Group views by",strat_group_area:"By area (default)",strat_group_status:"By status (Overdue / Triggered / Due Soon / OK)",strat_group_floor:"By floor (uses HA floors)",strat_group_due_date:"By due date (Overdue / Today / Week / Month / Later)",strat_group_calendar:"Rolling calendar (Week / Fortnight / Month / Year)",strat_editor_help:'The "Overview" view is always first. Empty groups are skipped.',picker_name_calendar:"Maintenance Supporter \u2014 Calendar",picker_name_panel:"Maintenance Supporter \u2014 Panel",picker_name_budget:"Maintenance Supporter \u2014 Budget",picker_name_groups:"Maintenance Supporter \u2014 Groups",picker_name_vacation:"Maintenance Supporter \u2014 Vacation",picker_name_battery_fleet:"Battery Fleet",picker_name_section:"Maintenance Supporter \u2014 Section",picker_name_vacation_status:"Maintenance Supporter \u2014 Vacation Status",picker_name_budget_status:"Maintenance Supporter \u2014 Budget Status",picker_desc_card:"Overview of your maintenance tasks with quick actions.",picker_desc_calendar:"Rolling calendar of maintenance tasks with 7/14/30/365 day windows, source icons, and prediction-confidence pills.",picker_desc_panel:"The complete Maintenance Supporter panel as a card \u2014 for a panel view / dashboard subview without the sidebar entry.",picker_desc_budget:"Edit the monthly and yearly budget right on the dashboard.",picker_desc_groups:"Create, rename and delete task groups right on the dashboard.",picker_desc_vacation:"Switch vacation mode on or off and set its dates right on the dashboard.",picker_desc_battery_fleet:"All tracked batteries: what is low now, what runs out soon, and what to buy.",picker_desc_dashboard:"Auto-generated dashboard. Group views by area, status, floor, or due date \u2014 picked from the strategy editor or YAML.",picker_desc_section:"Embed maintenance tasks (filterable by area, status, due date) as a section in any dashboard view.",picker_desc_vacation_status:"Compact vacation-mode status banner. Tap to open settings in the panel.",picker_desc_budget_status:"Monthly + yearly maintenance budget overview. Tap for details.",picker_desc_groups_status:"List of configured task groups with member counts.",hist_note_button:"Completed from dashboard button",hist_note_todo:"Completed from the To-do list",hist_note_voice:"Completed by voice",hist_note_notification:"Completed from the notification",hist_note_nfc:"Completed via NFC tag",hist_note_shopping_list:"Completed from the shopping list",hist_note_todo_mirror:"Completed from a mirrored to-do list",hist_note_skipped_notification:"Skipped from notification",hist_note_skipped_button:"Skipped from dashboard button",hist_note_sensor_triggered:"Sensor trigger activated",hist_note_initial:"Initial value set during task creation",hist_note_auto_recovered:"Auto-completed: sensor recovered ({value})",hist_note_reset:"Reset to {date}",hist_note_trigger_replaced:"Trigger entity replaced: {old} \u2192 {new}",hist_note_trigger_removed:"Sensor trigger removed (entity was: {entity}). Schedule converted to {schedule}.",hist_note_compound_removed:"Entity {entity} removed from compound trigger; {count} conditions remain.",recent_activities:"Recent Activities",search_notes:"Search notes",avg_cost:"Avg Cost",current:"Current",shorter:"Shorter",longer:"Longer",normal:"Normal",disabled:"Disabled",compound_logic:"Compound logic",compound:"Compound (multiple conditions)",compound_logic_and:"AND \u2014 all conditions must trigger",compound_logic_or:"OR \u2014 any condition triggers",compound_help:"Combine several sensor conditions into one trigger.",compound_no_conditions:"No conditions yet \u2014 add at least one.",compound_add_condition:"Add condition",compound_condition:"Condition",compound_remove_condition:"Remove condition",card_title:"Title",card_show_header:"Show header with statistics",card_show_actions:"Show action buttons",card_action_style:"Complete button style",card_compact:"Compact mode",card_max_items:"Max items (0 = all)",card_filter_status:"Filter by status",card_filter_status_help:"Empty = show all statuses.",card_filter_objects:"Filter by objects",card_filter_objects_help:"Empty = show all objects.",card_filter_areas:"Filter by areas",card_filter_areas_help:"Empty = show all areas.",card_filter_priority_help:"Empty = show all priorities. Tasks without an explicit priority count as Normal.",card_filter_entities:"Filter by entities (entity_ids)",card_filter_entities_help:"Pick sensor / binary_sensor entities from this integration. Empty = all.",card_loading_objects:"Loading objects\u2026",card_load_error:"Could not load objects \u2014 check the WebSocket connection.",card_no_tasks_title:"No maintenance tasks yet",card_no_tasks_cta:"\u2192 Create one in the Maintenance panel",no_objects:"No objects yet.",action_error:"Action failed. Please try again.",area_id_optional:"Area (optional)",installation_date_optional:"Installation date (optional)",warranty_expiry_optional:"Warranty expiry (optional)",warranty:"Warranty",warranty_valid_until:"valid until {date}",warranty_expires_in:"expires in {days} days",warranty_expired:"expired",cal_past_windows:"Past windows",cal_forward_windows:"Forward windows",history_edit_title:"Edit history entry",history_delete_entry:"Delete entry",history_delete_confirm:"Delete this history entry? The task's last-performed date falls back to the previous completion; photos stay with the object and consumed parts are not restocked.",history_edit_timestamp:"Timestamp",manufacturer:"Manufacturer",model:"Model",area:"Area",actions:"Actions",view_mode_label:"View",view_cards:"Card view",view_table:"Table view",objects_table_columns_label:"Objects table columns",objects_table_columns_hint:"Choose which columns appear in the objects table view.",custom_icon_optional:"Icon (optional, e.g. mdi:wrench)",notify_icon:"Notification icon",notify_icon_hint:"Shown by the Companion app on Android. Empty = the icon of this task's maintenance type ({default}).",task_enabled:"Task enabled",skip_reason_prompt:"Skip this task?",reason_optional:"Reason (optional)",reset_date_prompt:"Mark task as performed?",reset_date_optional:"Last performed date (optional, defaults to today)",notes_label:"Notes",documentation_label:"Documentation",no_nfc_tag:"\u2014 No tag \u2014",dashboard:"Dashboard",tab_today:"Today",palette_placeholder:"Search objects, tasks, spare parts, documents, notes\u2026",palette_no_results:"No matches",palette_hint:"\u2191\u2193 to navigate \xB7 Enter to open \xB7 Esc to close \xB7 / opens the search anywhere",search_open:"Search",search_group_parts:"Spare parts",search_group_content:"In documents",search_group_history:"History notes",search_page:"Page {page}",search_empty_hint:"Type to search objects, tasks, spare parts, documents and notes",search_searching:"Searching documents and notes\u2026",search_index_status:"Full-text search: {indexed} of {total} files indexed \xB7 {no_text} without text layer \xB7 {pending} pending",today_all_caught_up:"All caught up! Nothing due this week.",today_overdue:"Overdue",today_due_today:"Due today",today_this_week:"This week",settings:"Settings",settings_features:"Advanced Features",settings_features_desc:"Enable or disable advanced features. Disabling hides them from the UI but does not delete data.",feat_adaptive:"Adaptive Scheduling",feat_adaptive_desc:"Learn optimal intervals from maintenance history",feat_seasonal:"Seasonal Adjustments",feat_seasonal_desc:"Adjust intervals based on seasonal patterns",feat_environmental:"Environmental Correlation",feat_environmental_desc:"Correlate intervals with temperature/humidity",feat_budget:"Budget Tracking",feat_budget_desc:"Track monthly and yearly maintenance spending",feat_groups:"Task Groups",feat_groups_desc:"Organize tasks into logical groups",feat_checklists:"Checklists",feat_checklists_desc:"Multi-step procedures for task completion",settings_general:"General",settings_default_warning:"Default warning days",settings_consumable_threshold:"Consumable low threshold (%)",settings_battery_low_percent:"Battery low threshold (%)",settings_battery_recovered_percent:"Battery counts as replaced above (%)",settings_battery_recovered_percent_hint:"A low battery stays counted as low until its level rises above this value or a replacement is recorded \u2014 so a level hovering around the low threshold cannot complete the fleet task again and again.",settings_battery_auto_record:"Record a replacement automatically when a low battery recovers",settings_battery_auto_record_hint:"When a battery that was low rises above the recovery threshold, the replacement date is recorded in Battery Notes and the spare cells are taken from stock \u2014 no tap needed.",settings_battery_lifetimes:"Typical battery lifetimes",settings_battery_lifetimes_hint:'Only used for batteries without a percentage: a Battery Notes note that has just a type and a last-replaced date, or a sensor that only reports "low". Their due date is last replaced + this lifetime. Batteries that report a level get their forecast from the measured discharge instead. Once the fleet has seen three replacements of a type, the median interval takes over from the built-in value \u2014 your own value always wins.',settings_battery_lifetime_months:"months",settings_battery_lifetime_reset:"Use default",settings_battery_lifetime_in_fleet:"in your fleet",settings_battery_lifetime_learned_models:"Learned per model: {list}",settings_thresholds_hint:"Defaults for new Suggested setups (percent-remaining consumables) and for fleet batteries without their own Battery Notes threshold. Existing tasks keep their value.",settings_part_search_url:"Shopping search link",settings_part_search_url_hint:"URL with {q} in place of the search terms, used for parts without a product link. Leave empty for the automatic store (by country, then by language).",settings_value_out_of_range:"Value must be between {min} and {max}",bn_summary:"{name}: {pct} % for {n} batteries",bn_floor_decides:"Your {floor} % floor decides for all of them; the higher of the two thresholds counts per battery.",bn_above_floor:"That is above your {floor} % floor, so {name} decides for all noted batteries.",bn_overrides:"{n} devices with their own threshold:",bn_more:"+ {n} more",settings_row_actions:"Task row actions",settings_ref_numbers_in_lists:"Reference numbers in lists",settings_ref_numbers_in_lists_hint:"Show the #8 / #8.3 chips in front of object and task names in the Today list, the task table, the object cards and the objects table.",settings_compact_refs:"Compact reference numbers",settings_compact_refs_hint:"Renumbers all objects, tasks and completions in order (1, 2, 3 \u2026) and closes the gaps left by deletions. Numbers already printed on service booklets, photos or notes will no longer match.",settings_compact_refs_confirm:"Renumber every object, task and completion sequentially in creation order? Numbers already written on printed service booklets, photos or notes will no longer match. This cannot be undone.",settings_compact_refs_done:"Renumbered {objects} objects, {tasks} tasks and {completions} completions.",row_actions_buttons_compact:"Buttons (icons only on phones)",row_actions_buttons:"Buttons with text",row_actions_icons:"Icons (classic)",row_actions_follow:"Follow the household setting",settings_panel_enabled:"Sidebar panel",settings_panel_title:"Sidebar panel title",settings_notifications:"Notifications",settings_notify_service:"Notification service",settings_shopping_list:"Shopping list (buy tasks)",settings_shopping_list_help:"Low-part buy reminders appear in this to-do list; checking one off restocks the part.",shopping_list_none:"Off \u2014 no shopping list",settings_install_assist_sentences:"Install Assist sentences",settings_install_assist_sentences_hint:"Copies the voice sentences into your configuration so the classic Assist agent recognises them. A file you edited yourself is never overwritten.",test_notification:"Test notification",send_test:"Send test",testing:"Sending\u2026",test_notification_success:"Test notification sent",test_notification_failed:"Test notification failed",notify_per_person:"Per-person delivery",member_avatars:"Member avatars",member_avatars_hint:"Shown next to assigned tasks \u2014 initials in the member's colour. Defaults come from the name; set your own initials or pick a colour to tell members apart.",member_initials:"Initials",member_color:"Colour",member_avatar_reset:"Reset",notify_no_own_device:"No own device \u2014 uses the household service",settings_notify_due_soon:"Notify when due soon",settings_notify_overdue:"Notify when overdue",settings_notify_triggered:"Notify when triggered",settings_interval_hours:"Repeat interval (hours, 0 = once)",settings_quiet_hours:"Quiet hours",settings_quiet_start:"Start",settings_quiet_end:"End",settings_max_per_day:"Max notifications per day (0 = unlimited)",settings_bundling:"Bundle notifications",settings_bundle_threshold:"Bundle threshold",settings_reminder_leads:"Extra reminders (days before due)",settings_reminder_leads_hint:"Comma-separated lead times, e.g. 14, 3, 0 \u2014 one extra reminder fires on each matching day. Empty = off.",settings_actions:"Mobile Action Buttons",settings_action_complete:"Show 'Complete' button",settings_action_skip:"Show 'Skip' button",settings_action_snooze:"Show 'Snooze' button",settings_weekly_digest:"Weekly digest",settings_weekly_digest_hint:"A single summary notification on Monday morning when tasks are due.",settings_warranty_reminder:"Warranty expiry reminder",settings_warranty_reminder_days:"Days before expiry",settings_warranty_reminder_hint:"Notify once when an object's warranty is this many days from expiring.",settings_snooze_hours:"Snooze duration (hours)",settings_snooze_hours_hint:"How long Snooze mutes a task's reminders \u2014 from the task menu, a card, voice or the phone's Snooze button. The due date stays as it is.",settings_budget:"Budget",settings_currency:"Currency",settings_currency_decimals:"Decimal places for amounts",settings_currency_decimals_hint:"How many decimals every amount shows: KPIs, budgets, costs in lists and reports, budget alerts. 0 = whole numbers.",settings_parts_cost_mode:"Spare parts count as spending",settings_parts_cost_purchase:"When bought",settings_parts_cost_use:"When used",settings_parts_cost_mode_hint:"When bought: a buy reminder's cost counts, and the parts a job uses are shown as information. When used: every completion books the value of the parts it used, and a purchase sets the part's price instead of counting. Applies to completions from now on \u2014 history keeps how it was booked.",settings_budget_monthly:"Monthly budget",settings_budget_yearly:"Yearly budget",settings_budget_alerts:"Budget alerts",settings_budget_threshold:"Alert threshold (%)",settings_import_export:"Import / Export",settings_export_json:"Export JSON",settings_export_yaml:"Export YAML",settings_export_csv:"Export CSV",settings_export_settings:"Export settings (JSON)",settings_import_placeholder:"Paste JSON or CSV content here\u2026",settings_import_btn:"Import",settings_import_success:"{count} objects imported successfully.",settings_import_unmatched_users:"Not found on this instance: {names}. Their task assignments were removed \u2014 add people with the same names in Home Assistant and import again to keep them.",settings_export_success:"Export downloaded.",settings_saved:"Setting saved.",settings_include_history:"Include history",settings_export_selection:"Limit to selected objects (optional)",settings_docs_archive:"Documents archive (with files)",settings_docs_archive_hint:"The JSON and YAML exports list your documents but not their files; CSV leaves documents out. This ZIP holds the uploaded files \u2014 restore it after importing the JSON and nothing is missing.",settings_docs_export_btn:"Download documents ZIP",settings_docs_import_btn:"Restore documents ZIP",settings_docs_import_success:"Restored: {blobs} files, {docs} documents",settings_docs_import_missing:"{n} skipped \u2014 their files were not in the ZIP",sort_alphabetical:"Alphabetical",sort_due_soonest:"Due soonest",sort_task_count:"Task count",sort_area:"Area",sort_assigned_user:"Assigned user",sort_group:"Group",groupby_none:"No grouping",groupby_area:"By area",groupby_group:"By group",groupby_user:"By user",groupby_object:"By object",filter_label:"Filter",user_label:"User",photo_label:"Photo",sort_label:"Sort",group_by_label:"Group by",state_value_help:'Use the HA state value (usually lowercase, e.g. "on"/"off"). Case is normalised on save.',target_changes_help:"Number of matching transitions before the trigger fires (default: 1).",state_latch_help:"With 1 transition the trigger is a latch: it recovers when the entity leaves the To-state \u2014 or, with only a From-state set, when it returns to that state.",for_minutes_state_help:"0 counts every change immediately. Set minutes and the new state must hold that long first \u2014 brief flickers then neither trigger nor count.",qr_print_title:"Print QR codes",qr_print_desc:"Generate a printable page of QR codes to cut out and stick on your equipment.",qr_print_load:"Load objects",qr_print_filter:"Filter",qr_print_objects:"Objects",qr_print_actions:"Actions",qr_print_url_mode:"Link type",qr_print_estimate:"Estimated QR codes",qr_print_over_limit:"cap is 200, narrow the filter",qr_print_generate:"Generate QR codes",qr_print_generating:"Generating\u2026",qr_print_ready:"QR codes ready",qr_print_print_button:"Print",qr_print_empty:"Nothing to generate",qr_action_skip:"Skip",vacation_title:"Vacation mode",vacation_active:"active",vacation_ended:"ended",vacation_desc:"Plan a vacation: notifications are paused during the period plus a buffer of days. You can opt specific tasks back in.",vacation_enable:"Enable vacation mode",vacation_start:"Start",vacation_end:"End",vacation_buffer:"Buffer (days)",vacation_exempt_title:"Notify anyway during vacation",vacation_exempt_desc:"Pick tasks that should still notify during vacation (e.g. critical pool chemistry).",vacation_load_tasks:"Load tasks",vacation_preview_btn:"Show preview",vacation_preview_affected:"tasks affected",vacation_event_due_soon:"becomes due soon",vacation_event_overdue:"becomes overdue",vacation_event_triggered_est:"sensor trigger possible",vacation_sensor_based:"(sensor-based)",vacation_action_notify:"Notify anyway",vacation_action_unsilence:"Silence again",vacation_marked_complete:"Marked complete",vacation_marked_skip:"Skipped",vacation_end_now:"End vacation now",add:"Add",show_stats:"Show stats + graphs",hide_stats:"Hide stats",adaptive_no_data:"Not enough completion history yet for adaptive analysis. Complete this task a few more times to unlock interval recommendations and reliability charts.",suggestion_applied:"Suggested interval applied",vacation_mode:"Vacation mode",vacation_status_active:"Active now",vacation_status_scheduled:"Scheduled",vacation_status_inactive:"Inactive",vacation_end_now_confirm:"End vacation immediately?",vacation_exempt_count:"exempt",vacation_advanced:"Advanced\u2026",vacation_open_panel:"Open in panel",enable:"Enable",saved:"Saved",budget_monthly_set:"Set monthly",budget_yearly_set:"Set yearly",budget_advanced:"Currency, alerts\u2026",budget_open_panel:"Open in panel",groups_empty:"No groups yet.",group_new_placeholder:"Add group\u2026",groups_manage_tasks:"Manage task assignments\u2026",groups_open_panel:"Open in panel",unassigned:"Unassigned",no_area:"No area",has_overdue:"Has overdue tasks",object:"Object",settings_panel_access:"Panel access",settings_panel_access_desc:"Admins always have full access. To delegate create, edit and delete to specific non-admins, switch this on and pick them below \u2014 everyone else only gets Complete, Skip, Reset, Postpone and Snooze.",settings_operator_write:"Allow selected users to create, edit & delete",settings_operator_write_desc:"Off: only admins can change content. On: the selected users below get full access too.",no_non_admin_users:"No non-admin users found. Add some in Settings \u2192 People.",owner_label:"Owner",feat_completion_actions:"Completion actions",feat_completion_actions_desc:"Per-task HA action on complete + quick-complete QR with pre-set values.",on_complete_action_title:"On complete: trigger HA action (optional)",on_complete_action_desc:"Calls an HA service when the task is completed \u2014 e.g. reset a counter on the device.",on_complete_action_target:"Target entity",on_complete_action_target_hint:"Note: the entity domain must match the service \u2014 e.g. 'button.press' only works on button.*, 'counter.increment' only on counter.*, 'input_button.press' only on input_button.* etc. On a mismatch the action will silently fail (HA logs 'Referenced entities ... missing or not currently available').",on_complete_action_target_more:"This action targets more than this field shows: {targets}. That is kept as it is \u2014 choosing another entity here replaces the whole target.",on_complete_action_skip_auto:"Not when the task completes itself (its sensor recovered)",on_complete_action_data:"Data (JSON, optional)",on_complete_action_test:"Validate configuration",on_complete_action_test_success:"\u2713 Configuration valid (action will fire only on task completion)",on_complete_action_test_failed:"Failed",action_test_bad_format:"Invalid service format (expected 'domain.service')",action_test_unknown_service:'Service "{service}" is not registered in Home Assistant. Check the spelling and that the integration providing it is loaded.',action_test_domain_mismatch:`Service "{service}" only works on {domain}.* entities; entity "{entity}" is in {entity_domain}.* \u2014 pick a service that matches the entity's domain (e.g. {suggestion})`,action_test_entity_missing:'Target entity "{entity}" was not found in Home Assistant \u2014 it may have been renamed or its integration removed.',quick_complete_defaults_title:"Quick-complete defaults (for QR scans, optional)",quick_complete_defaults_desc:"Pre-set values for quick-complete QR scans. Without these, the QR opens the complete dialog.",quick_complete_defaults_notes:"Notes",quick_complete_defaults_cost:"Cost",quick_complete_defaults_duration:"Duration (minutes)",quick_complete_defaults_feedback_none:"No feedback",quick_complete_defaults_feedback_needed:"Was needed",quick_complete_defaults_feedback_not_needed:"Not needed",quick_complete_success:"Quickly marked complete",show_all_objects:"Show all objects",show_all_tasks:"Clear filter \u2014 show all tasks",filter_to_overdue:"Filter task list to overdue only",filter_to_due_soon:"Filter task list to due-soon only",filter_to_triggered:"Filter task list to triggered only",open_task:"Open task",show_details:"Show history + stats",hide_details:"Hide details",history_empty:"No history yet.",history_edit_button:"Edit entry",total_cost:"Total cost",times_performed:"Performed",older_entries:"older",open_in_panel:"Open in Maintenance panel",skip_reason:"Skip reason (optional)",reset_to_date:"Reset last_performed to",delete_task_confirm:"Delete this task and its history?",delete_object_confirm:"Delete this object and all its tasks?",loading:"Loading\u2026",archive:"Archive",undo:"Undo",task_archived:"Task archived",object_archived:"Object archived",unarchive:"Unarchive",archived:"Archived",show_archived:"Show archived",hide_archived:"Hide archived",section_collapse:"Collapse section",section_expand:"Expand section",confirm_archive_object:"Archive this object and its tasks? They keep their history and can be unarchived later.",settings_archive:"Archive & Retention",settings_archive_desc:"Retire completed one-off tasks without deleting them. Archived items are hidden and inert but keep their history and cost.",settings_archive_oneoff_days:"Auto-archive completed one-off tasks after (days, 0 = off)",settings_delete_archived_oneoff_days:"Auto-delete archived one-off tasks after (days, 0 = never)",archive_object:"Archive object",unarchive_object:"Unarchive object",documents:"Documents",documents_empty:"No documents yet.",doc_upload:"Upload file",doc_uploading:"Uploading\u2026",doc_add_link:"Add link",doc_link_url:"URL (https://\u2026)",doc_link_title:"Title (optional)",doc_open:"Open",doc_delete_confirm:'Delete "{name}"?',doc_too_large:"File is too large (max 25 MB).",doc_upload_failed:"Upload failed.",docs_archive_too_large:"The archive is too large (max {max}).",docs_archive_no_manifest:"This ZIP has no manifest.json, so the restore cannot tell which document a file belongs to. Keep manifest.json next to the folders when zipping again.",completion_photos_optional:"Completion photos (optional)",completion_photos:"Completion photos",choose_photos:"Choose photos",choose_photo:"Choose photo",photos_android_hint:"The Android app hands over one photo per pick \u2014 each pick is added.",photos_limit:"Up to {max} photos per completion",history_edit_photos_hint:"Removing a photo here keeps the file in the object's documents.",uploading:"Uploading\u2026",remove:"Remove",doc_deduped:"Already stored elsewhere \u2014 shared, no extra space used.",doc_dup_in_object:"This file is already attached to this object.",doc_link_invalid:"Only http/https links are allowed.",doc_cat_manual:"Manual",doc_cat_warranty:"Warranty",doc_cat_invoice:"Invoice",doc_cat_spare_parts:"Spare parts",doc_cat_photo:"Photo",doc_cat_other:"Other",doc_link_badge:"Link",doc_storage_title:"Document storage",doc_storage_saved:"Saved via deduplication",doc_storage_refresh:"Refresh",doc_download:"Download",doc_close:"Close",doc_camera:"Take photo",camera_capture_shoot:"Capture",camera_switch_lens:"Switch camera",camera_switching:"Switching camera\u2026",camera_not_answered:"This camera did not answer. Tap the switch again to try the next one, or use the file picker.",camera_diagnostics:"Details for a bug report",camera_copy:"Copy",camera_copied:"Copied",doc_drop_hint:"Drop files here",doc_task_none:"No documents linked to this task.",doc_link_existing:"Link a document\u2026",doc_attach:"Link",doc_unlink:"Unlink",doc_page:"Page",chart_range_7d:"7d",chart_range_30d:"30d",chart_range_90d:"90d",chart_range_1y:"1y",chart_since_service:"since last service",chart_no_stats:"No long-term statistics for this entity \u2014 showing maintenance-event values only",auto_complete_on_recovery:"Auto-complete when the sensor recovers",auto_complete_on_recovery_help:"Records a completion (sets last performed) when the trigger clears itself \u2014 e.g. salt refilled, filter replaced.",doc_search:"Search documents\u2026",doc_search_none:"No matching documents",doc_description:"Description (optional)",doc_sort:"Sort documents",doc_sort_newest:"Newest first",doc_sort_oldest:"Oldest first",doc_sort_title:"By title (1, 2, 3 \u2026)",doc_sort_category:"By category",link_device_optional:"Link to existing device (optional)",parent_object_optional:"Parent object (optional)",parent_none:"(No parent)",paused:"Paused",pause_object:"Pause",resume_object:"Resume",pause_task:"Pause",resume_task:"Resume",pause_task_prompt:"Freeze this task's schedule \u2014 it does not become due and does not notify until you resume it, and resuming starts a fresh cycle from that day. The object's other tasks keep running. Optionally set a date to resume by itself.",task_paused:"Task paused",task_resumed:"Task resumed \u2014 its schedule restarted",pause_until_prompt:"Freeze this object's schedules \u2014 nothing becomes due and nothing notifies until it is resumed. Optionally set an auto-resume date.",pause_until_label:"Resume on (optional)",object_paused:"Object paused",object_resumed:"Object resumed \u2014 schedules restarted",object_paused_badge:"Paused",paused_until_label:"until",replace_object:"Replace\u2026",replace_object_prompt:"Retire this object and create a successor. History and costs stay archived on the old one; tasks and documents carry over to the new one, counters start fresh.",replace_name_label:"Successor name",replace_device_heading:"Device in Home Assistant",replace_device_keep:"Same device as before ({device})",replace_device_keep_hint:"Also the choice when the new unit is not in Home Assistant yet: once it is, pick it as the object's device in its settings \u2014 the sensor triggers move along then.",replace_device_other:"The new unit is another device",replace_device_pick:"Device of the new unit",replace_device_other_hint:"Sensor triggers, completion actions and the counter reset move to the matching entities of that device.",replace_device_none:"No device",object_replaced:"Object replaced \u2014 successor created",device_swap_moved:"{count} sensor links moved to the new device.",device_swap_moved_one:"One sensor link moved to the new device.",device_swap_unmatched:"{count} could not be matched \u2014 check those tasks' sensor triggers.",device_swap_unmatched_one:"One could not be matched \u2014 check that task's sensor trigger.",reading_unit_label:"Reading unit (e.g. kWh, m\xB3)",reading_unit_help:"Shown next to the recorded value when completing this task.",reading_value_label:"Reading value",reading_label:"Reading",readings_section:"Readings",readings_hint:"Several named values per completion \u2014 one row per meter. Leave empty to record a single value.",reading_name_label:"Reading name",reading_unit_short:"Unit",reading_add:"Add reading",reading_duplicate_name:"Name already used",reading_last:"last: {value}",reading_below_last:"Lower than the last reading ({value})",settings_templates_label:"Template gallery",settings_templates_hint:`Untick templates you'll never need \u2014 they disappear from the "From template" pickers (panel and config flow). Nothing else changes; you can re-enable them any time.`,home_profile_title:"Home profile",home_profile_hint:"Recommends templates and sets seasonal windows for your climate and hemisphere. Everything is worked out locally from your floors, areas and home location.",home_type_label:"Home type",home_type_auto:"Automatic ({detected})",home_region_label:"State, province or region",home_region_none:"not found",home_region_hint:"Some rules differ within a country \u2014 a yearly vehicle inspection in one state, none in the next. Found from your home location.",home_dwelling_house:"House",home_dwelling_apartment:"Apartment",home_dwelling_unknown:"not recognised",home_detected_from:"Detected from: {reasons}",home_reason_floors:"several floors",home_reason_basement:"a basement",home_reason_garden:"a garden",home_reason_shed:"a shed",home_reason_driveway:"a driveway",home_reason_garage:"a garage",home_reason_attic:"an attic",home_reason_lawn_mower:"a lawn mower",home_reason_gate:"a gate",home_reason_balcony:"a balcony",home_reason_small_home:"few rooms on one floor",home_reason_ups:"a UPS",home_climate_label:"Climate",home_climate_value:"K\xF6ppen {koppen} \xB7 coldest month {cold} \xB0C \xB7 warmest month {warm} \xB0C",home_climate_unknown:"No home location set in Home Assistant",home_hemisphere_south:"Southern hemisphere \u2014 seasonal windows are mirrored",home_trait_freeze:"Frost in winter",home_trait_snow:"Snowy winters",home_trait_severe_winter:"Severe winters",home_trait_damp:"Damp climate",home_trait_mediterranean:"Mediterranean climate",home_trait_earthquake:"Earthquake region",home_trait_hot_summer:"Hot summers",home_trait_hot_humid:"Hot and humid",home_trait_hot_dry:"Hot and dry",home_trait_termites:"Termite region",home_trait_wildfire:"Wildfire risk",home_trait_cyclone:"Hurricane / typhoon region",home_trait_tropical:"Tropical climate",home_trait_radon:"Radon test recommended",home_reason_starter:"Basics for your home",home_reason_country:"Common in {country}",home_reason_feature:"Found in your home: {feature}",templates_recommended_title:"Recommended for your home",templates_recommended_hint:"Based on your home profile \u2014 adjust it in Settings.",templates_legal_hint:"Notes on legal duties and intervals are guidance, not legal advice. Rules vary by region and change over time \u2014 local regulations and the manufacturer's instructions take precedence.",templates_not_typical:"Not typical for your home type",templates_set_up:"Already set up",worksheet:"Work sheet",worksheet_scan_view:"Scan to open the task",worksheet_scan_complete:"Scan to complete",worksheet_manual_excerpt:"Manual excerpt",worksheet_pages:"pages",worksheet_printed:"Printed",worksheet_never:"Never",card_all_caught_up:"All caught up \u2014 nothing needs attention",postpone:"Postpone",postpone_date_prompt:"Postpone this occurrence to which date?",postpone_date_label:"New due date",postponed:"Postponed",postponed_to:"Postponed to",season_window_label:"Seasonal window (months)",season_window_hint:"Only due in the selected months; off-season dates roll to the next active month. None = all year.",series_end_label:"Ends",series_end_never:"Never (repeats indefinitely)",series_end_after_count:"After a number of times",series_end_until:"On a date",series_end_count_label:"Number of times",series_end_until_label:"End date",parts_section:"Parts & consumables",parts_inventory_value:"Inventory value",part_add:"Add part",part_name:"Name",part_vendor:"Manufacturer",part_storage_location:"Storage location",part_product_url:"Product URL",part_unit:"Unit",part_cost:"Unit price",part_stock:"Stock",part_reorder_threshold:"Reorder at",part_restock_quantity:"Restock quantity",part_package_size:"Package size (units)",part_restock_packages:"Restock amount (packages)",part_package_hint:"Bought in packages, used in the unit: stock, the amount a task uses and the reorder threshold count units; the restock amount and the price are per package.",part_auto_buy:"Auto-create buy task when low",part_restock:"Adjust stock",parts_used_by:"Used by",restock_quantity_label:"Quantity bought",consumes_parts_label:"Consumes parts",task_parts_fleet_hint:"Battery quantities come from Battery Notes per device \u2014 nothing to link here.",shared_parts_other_objects:"Parts from other objects",shared_parts_help:"Several objects can share one stock. Completing this task takes from the owning object.",shared_part_unknown:"Unknown part",parts_load_failed:"Couldn't load this object's parts \u2014 the consumes-parts options are unavailable right now.",adopt_problem_button:"Adopt problem sensors",adopt_problem_title:"Adopt problem sensors",adopt_problem_hint:"Turn HA problem sensors (printer errors, filter warnings, low battery) into maintenance tasks that trigger while the problem is active and clear themselves when it resolves.",adopt_problem_none:"No problem sensors found that aren't already tracked.",adopt_problem_active:"active",adopt_problem_ok:"ok",adopt_problem_new_object:"(new)",adopt_object_hint:"Give several sensors the same object name and they become one object; type an existing object's name to add them there.",adopt_summary_title:"What adopting changes",adopt_summary_existing:'"{name}": tasks {before} \u2192 {after}',adopt_problem_adopt:"Adopt selected",adopt_problem_done:"Adopted {tasks} problem sensor(s)",adopt_problem_done_one:"Adopted 1 problem sensor",views_label:"Views",views_none:"\u2014 No view \u2014",views_manage:"Save / manage views",views_dialog_title:"Saved views",views_dialog_hint:"Save the current filters as a named view everyone can reuse.",views_name_placeholder:"View name",views_save_current:"Save current filters",views_none_yet:"No saved views yet.",close:"Close",trigger_hint_now:"The sensor reads {value} right now.",trigger_hint_above:"The task triggers once it rises above {target}.",trigger_hint_below:"It triggers once it falls below {target}.",trigger_hint_overlap:"These limits overlap: every reading triggers the task and it can never recover. Leave one of them empty.",trigger_hint_counter_delta:"Counts from the current reading ({value}): due at {due} (+{target}), and the count restarts after each completion.",trigger_hint_counter_delta_edit:"Counts usage since the last completion: due after +{target}; the count restarts after each completion.",trigger_hint_counter_abs:"The task becomes due once the sensor reaches {target}.",trigger_hint_runtime:"The task becomes due after {hours} h of accumulated on-time; the counter restarts after each completion.",trigger_hint_due_date:"The task becomes due {days} days before the date the sensor reports.",trigger_hint_due_date_one:"The task becomes due one day before the date the sensor reports.",trigger_hint_due_date_same_day:"The task becomes due on the date the sensor reports.",trigger_hint_due_date_now:"Reported date: {date}.",trigger_hint_state_change:"The task becomes due after {count} state change(s).",trigger_hint_state_change_to:"The task becomes due after {count} change(s) to \u201C{state}\u201D.",trigger_hint_state_now:"Current state: {value}.",adopt_problem_part:"Uses part: {name}",label_filter:"Label",all_labels:"All labels",settings_notify_scope:"Notify only for view",settings_notify_scope_all:"All tasks",settings_notify_scope_hint:"Only tasks matching the selected saved view's label/user filters send reminders. Status, sorting and grouping of the view are ignored here.",settings_notify_completed:"Completion notifications",settings_notify_completed_hint:`Household news when a task is completed, with the reason and who did it. "Automatic only" announces just the completions nobody made on the spot: sensor recovery, shopping-list check-off, an automation's service call. Sent to the household service only; quiet hours and the daily cap apply.`,settings_title_style:"Notification title style",settings_title_style_hint:"What appears as the notification's TITLE. Default keeps the per-status text (e.g. 'Maintenance overdue!'). Object name / Task name help when phone notifications stack.",title_style_default:"Default (per-status title)",title_style_object_name:"Object name as title",title_style_task_name:"Task name as title",notify_completed_off:"Off",notify_completed_automatic:"Automatic completions only",notify_completed_all:"All completions",settings_notify_rule:"Your own notification rule",settings_notify_rule_hint:"Every notification also fires the event maintenance_supporter_notification (kind, status, object, task, reference numbers, priority, deep link, the payload). Route it with an automation \u2014 e.g. into Ticker, Telegram or Pushover.",settings_notify_event_only:"Only fire the event, send nothing myself",settings_notify_event_only_hint:"Your automation is the whole delivery. Rate limits, quiet hours and the daily cap still apply to when the event fires.",settings_notify_extra_data:"Extra notification data (template)",settings_notify_extra_data_hint:"JSON or YAML, Jinja allowed; merged into the notify call's data (your keys win). Variables: kind, status, priority, labels, notes, area_name, object_name, task_name, object_ref, task_ref, days_until_due, next_due, last_performed, responsible_user_id, sensor_entity_id, url, target, title, message. Test it with the Send test button.",card_saved_view:"Saved view",card_saved_view_none:"None",card_saved_view_help:"Applies the view's status, user, label and priority filters on top of the filters above. The view's sorting and grouping are panel display settings and are not applied on the card.",panel_card_tab:"Open on tab",panel_card_tab_default:"Remembered tab (as in the panel)",panel_card_view:"Open with saved view",panel_card_height:"Height",panel_card_height_help:"Empty = fill the screen below the card (panel view). Otherwise a CSS length such as 600px or 80vh for views with other cards.",panel_card_not_registered:"The Maintenance Supporter panel is not available \u2014 is the integration set up?",panel_card_load_failed:"The Maintenance Supporter panel could not be loaded \u2014 reload the page (after an update: clear the browser cache).",doc_part_none:"No documents linked to this part.",settings_templates_toggle_group:"Enable or disable all templates in this group",setups_button:"Suggested setups",setups_title:"Suggested setups (Beta)",setups_hint:"Devices of supported integrations whose consumable sensors can drive maintenance tasks. Adopting creates the object and wires each task to its sensor \u2014 it triggers when the consumable runs low and resolves itself after replacement.",setups_none:"No supported devices with unwired consumable sensors found.",setups_adopt:"Set up selected",setups_done:"{tasks} sensor-wired tasks created.",setups_done_one:"1 sensor-wired task created.",complete_parts_used:"Parts used this time",part_delete_confirm:"Delete part '{name}'? Its stock tracking, task links and any open buy reminder will be removed.",baseline_start_value:"Start reading (optional)",baseline_start_help:"Counting starts from this reading. Leave empty to count from the current value; enter the reading at the last service so usage since then already counts.",setups_baseline_hint:"reading at last service (optional)",baseline_start_help_edit:"Leave empty to keep the existing counting. Entering a value re-anchors the counting (e.g. the reading at the last service).",baseline_current_effective:"Currently effective start value: {value}",runtime_on_states:"Active states",runtime_on_states_help:"States that count as running \u2014 default: on. E.g. mowing, cleaning, printing. With an attribute selected, its values are matched instead.",runtime_max_session:"Max session runtime (seconds)",runtime_max_session_help:"A single run counts at most this many seconds \u2014 protects against a sensor stuck ON (lost connection, restart). Empty = no cap.",trend_approaching:"Heading toward the threshold",trend_easing:"Easing away from the threshold",split_select_hint:"Select a task to see its details here",setups_target_new:"Create new: {name}",setups_target_match:"{name} \u2014 matches: {reasons}",setups_reason_model:"model number",setups_reason_name:"name",setups_reason_area:"area",setups_reason_sibling:"same model on another device",setups_maybe_covered:'Probably there already as "{name}"',setups_already:"Already there: {names}",setups_reset_line:"Completing it also resets the counter in the integration (\u201C{button}\u201D)",setups_reset_enable:"\u2014 the button gets switched on for this",reset_offers_title:"Reset counters on completion",reset_offers_hint:"These tasks watch a counter the integration keeps itself. Connected, completing the task here also presses the integration's reset button \u2014 otherwise the counter keeps running and the task falls due again right away.",reset_offers_before:"Now: completing it leaves the counter in {integration} running",reset_offers_after:"Then: it also presses \u201C{button}\u201D",reset_offers_renamed:"Renamed since it was set up \u2014 check that this task is for this part",reset_offers_apply:"Connect resets",reset_offers_done:"{count} tasks now reset their counter on completion",reset_offers_done_one:"1 task now resets its counter on completion",setups_before_after:"Tasks on the object: {before} \u2192 {after}",setups_new_object_count:'New object "{name}" \xB7 tasks: {count}',schedule_preview_title:"Next dates",schedule_preview_ontime:"Assuming on-time completion.",schedule_preview_ends:"(series ends)",adopt_problem_responsible:"Responsible user for all adopted tasks (optional)",adopt_for_minutes_hint:"Only trigger once the problem has persisted this long \u2014 0 reacts to the first flicker.",adopt_problem_configure:"Configure",history_auto:"Automatic",battery_fleet_title:"Battery fleet",battery_fleet_none_low:"All batteries OK \u2014 nothing to replace.",battery_fleet_progress:"{n} to replace",battery_fleet_buy_now:"Buy now",battery_fleet_soon:"Needed soon",battery_fleet_soon_hint:"Predicted from the last replacement date \u2014 order ahead.",battery_fleet_mark_all:"Mark all replaced",battery_fleet_mark_one:"Mark this battery replaced",battery_fleet_offline:"offline",battery_fleet_no_sensor:"No sensor",battery_fleet_trigger_lost:"This task's sensor trigger was lost \u2014 it will not fire or auto-complete.",battery_fleet_repair:"Repair",battery_fleet_exclude:"Exclude from the fleet",battery_fleet_excluded:"Excluded",battery_fleet_include:"Track again",battery_fleet_all:"All tracked batteries",battery_fleet_all_hint:"Exclude a device here to drop it from the fleet before it ever reports low \u2014 a vacuum that recharges itself, or a phone that warns you on its own.",battery_fleet_add:"Add a battery",battery_fleet_add_hint:"Pick a battery sensor the automatic discovery missed \u2014 it joins the roster immediately.",battery_fleet_track_self:"Track self-charging batteries",battery_fleet_track_self_hint:"Phones, vacuums and other devices that recharge themselves appear as rechargeables \u2014 a low one asks for a charge, never for new cells.",battery_fleet_due_without_sensor:"Treat a passed forecast as due when a battery has no sensor",battery_fleet_due_without_sensor_hint:"Some devices report no battery level at all \u2014 Battery Notes only knows the type and the last replacement. Once the typical lifetime has passed, such a battery counts as low and the fleet task reminds you; marking it replaced resets the forecast.",battery_fleet_status_low:"Low",battery_fleet_status_soon:"Soon",battery_fleet_status_ok:"Healthy",battery_fleet_status_due:"Due",battery_fleet_predicted_on:"Expected around {date}",battery_fleet_predicted_typical:"Expected around {date} \u2014 {months} months typical for {type} ({source})",lifetime_source_default:"built-in default",lifetime_source_table:"built-in table",lifetime_source_learned:"learned from {n} replacements",lifetime_source_override:"your setting",lifetime_source_learned_device:"learned from this device's {n} replacements",lifetime_source_learned_model:"learned from {n} replacements on devices of this model",battery_fleet_predicted_trend:"Predicted from this battery's discharge trend: around {date} ({confidence})",battery_fleet_rechargeable:"Rechargeable: charge instead of replacing \u2014 never on the shopping list",battery_fleet_sort_name:"Sort by name",battery_fleet_sort_urgency:"Sort by urgency",battery_fleet_mark_recharged:"Mark as recharged",battery_fleet_sparkline_hint:"Battery level over the last 30 days \u2014 dotted: projected until the low threshold",battery_fleet_filter_type:"Show only this battery type",battery_fleet_record_replacement:"The level jumped around {date} \u2014 record this replacement in Battery Notes",battery_fleet_total:"{n} batteries tracked",battery_fleet_setup_button:"Battery fleet",battery_fleet_setup_done:"Battery fleet set up \u2014 one task tracks all your batteries.",update_banner:"A newer version of Maintenance Supporter is on the server \u2014 reload to update the panel.",update_reload:"Reload",row_actions_banner:"Complete and Skip in task rows are now buttons. Prefer the old icons?",row_actions_keep:"Keep buttons",row_actions_back:"Back to icons",battery_fleet_forecast_overdue:"Predicted date passed \u2014 the battery still reports healthy. If you swapped it, record the replacement; otherwise the forecast was off.",cost_from_parts:"Use \u2248 {amount} from parts",cost_parts_booked:"Parts used \u2248 {amount} are booked automatically \u2014 enter only additional costs.",cost_parts_info:"Parts used \u2248 {amount} \u2014 counted when they were bought, not here.",cost_purchase_use_hint:"The price paid becomes the part's price \u2014 spare parts count when they are used.",history_parts_counted:"Parts: {amount}",history_parts_counted_hint:"Value of the parts used \u2014 counted as spending",history_parts_info:"Parts \u2248 {amount}",history_parts_info_hint:"Value of the parts used \u2014 information only; the money counted when they were bought",history_purchase_stock:"Bought for {amount} (counted when used)",dismiss:"Dismiss",gs_label:"Getting started \u2014 these hints retire as your setup grows",gs_setups_chip:"Suggested setups found {n} devices with pre-wired triggers",gs_setups_chip_one:"Suggested setups found 1 device with pre-wired triggers",gs_adopt_chip:"{n} problem sensors can become maintenance tasks",gs_adopt_chip_one:"1 problem sensor can become a maintenance task",gs_fleet_chip:"One click sets up the battery fleet",gs_reset_chip:"{n} tasks can reset their counter in the integration",gs_reset_chip_one:"1 task can reset its counter in the integration",cal_editor_window:"Default window",cal_editor_window_week:"Week (7 days)",cal_editor_window_fortnight:"Fortnight (14 days)",cal_editor_window_month:"Month (30 days, default)",cal_editor_window_year:"Year (365 days, empty days collapsed)",cal_editor_window_past_30:"Past 30 days (history)",cal_editor_window_past_90:"Past 90 days (history)",cal_editor_show_chips:"Show window chips inside the card",cal_editor_chips_hint:"Hide the chips when the card is embedded in a strategy view that already serves as the window selector.",cal_editor_show_user_filter:"Show user filter dropdown",cal_editor_default_user:"Default user filter",cal_editor_my_tasks:"My tasks (current user)",cal_editor_show_object_filter:"Show object filter dropdown",cal_editor_object_hint:'Pre-select one object via YAML: object_filter: "" \u2014 or a list of names to restrict the card to several objects.',object_history_section:"History (all tasks)",object_history_all_tasks:"All tasks",object_history_empty:"No entries in this range.",object_history_cap_note:"History keeps up to 500 entries per task \u2014 very old entries may be missing.",service_record_title:"Service record",service_record_print:"Service record (PDF)",date:"Date",service_record_entries:"entries",ref_number:"Reference number",print_options_title:"Print service record",print_layout:"Layout",print_layout_chronological:"Chronological",print_layout_by_task:"By task",print_include:"Include",print_inc_readings:"Readings",print_inc_parts:"Parts used",print_inc_photos:"Photos",print_inc_documents:"Linked documents",print_inc_doc_desc:"Document descriptions",print_inc_checklist:"Checklist",print_inc_notes:"Notes",print_inc_costs:"Cost and duration",print_inc_person:"Completed by",print_inc_refs:"Reference numbers",print_inc_bare:"Completions without details",print_inc_qr:"QR code per task",print_button:"Print",report_scan_hint:"Scan to open the task",completed_by:"Completed by",date_from:"From",date_to:"To",phases_section:"Cycle phases (optional)",phases_hint:"Different work on one shared schedule \u2014 each completion moves to the next step (e.g. small service, small service, big service).",phase_add:"Add phase",phase_name:"Phase name",phase_sequence_label:"Cycle order",phase_sequence_add_step:"Add step",phase_current:"Current phase",phase_set:"Set as current",chart_history_fallback:"No long-term statistics for this entity \u2014 showing recorder state history (typically the last ~10 days)",chart_history_alarm:"Trigger view from recorder state history: 1 = alert state held for the hold time, 0 = fine (typically the last ~10 days)",chart_history_count:"Change count rebuilt from recorder state history since the last service (typically the last ~10 days)",prediction_cycles:"Learned from cycles",phase_require_override:"Override \u201CRequire on completion\u201D for this phase",history_add_past:"Add past completion",require_tag_scan:"Only complete by scanning the tag",require_tag_scan_help:"Proof of presence: Done is blocked on every surface until the NFC tag or QR code on the thing itself is scanned. Automations can pass 'via_tag_scan' to the complete service.",disallow_skip:"Don't allow skipping",disallow_skip_help:"The Skip action disappears from every surface, and the server refuses a skip from automations and voice too.",no_notifications:"No notifications for this task",no_notifications_help:"No reminders for this task \u2014 no status notifications, repeats, lead-time reminders or bundles. It still shows on the dashboard and in its entities; the weekly digest still counts it.",require_tag_scan_hint:"This task completes only by scanning its NFC tag or QR code on the thing itself \u2014 saving here will be refused."}});var Ur,Br=y(()=>{"use strict";Ur="2.97.1"});function ki(s){Ee.en=Object.assign({},s,Ee.en??{})}function _e(s){let r=(s||wi).toLowerCase();return r.startsWith("pt")&&r.endsWith("br")?"pt-br":r.substring(0,2)}function n(s,r,e){let t=_e(r);return Ee[t]?.[s]??Ee.en?.[s]??e??s}function Se(s){let r=_e(s);return r===wi||r in Ee}function Te(s){let r=_e(s);return r===wi||r in Ee||!fa.has(r)?Promise.resolve():(r in ht||(ht[r]=fetch(`${va}/${r}.json?v=${Ur}`).then(e=>e.ok?e.json():null).then(e=>{e?Ee[r]=e:delete ht[r]}).catch(()=>{delete ht[r]})),ht[r])}var wi,Wr,Ee,ht,fa,va,Ot=y(()=>{"use strict";Br();wi="en",Wr=(()=>{let s=window;return s.__msLocales||(s.__msLocales={store:{},inflight:{}}),s.__msLocales})(),Ee=Wr.store,ht=Wr.inflight;fa=new Set(["de","nl","fr","it","es","pt","pt-br","ru","uk","pl","cs","sv","zh","da","fi","nb","ja","hi","hu","ko","tr"]),va="/maintenance_supporter_locales"});function Vr(s){let r=s?.language;!r||typeof s?.loadBackendTranslation!="function"||X.lang===r&&(X.localize||X.inflight)||(X.lang=r,X.localize=null,X.inflight=s.loadBackendTranslation("exceptions",$i).then(e=>{X.lang===r&&(X.localize=e)}).catch(()=>{}).finally(()=>{X.inflight=null}))}function Gr(s,r,e){if(!r||!X.localize||s&&s!==$i)return null;let t=`component.${$i}.exceptions.${r}.message`,i=X.localize(t,e??void 0);return i&&i!==t?i:null}var $i,X,Ei=y(()=>{"use strict";$i="maintenance_supporter",X=(()=>{let s=window;return s.__msBackendErrors??={lang:"",localize:null,inflight:null}})()});function Ce(s){return s?.currency_symbol||ya}function Kr(s,r){r.has("hass")&&Si(s.hass?.locale,s.hass?.config?.country);let e=s.hass?.language;e&&!Se(e)&&Te(e).then(()=>s.requestUpdate()),Vr(s.hass)}function L(s){return s?.language||"en"}function Yr(s){let r=_e(s);return{de:"de-DE",en:"en-US",nl:"nl-NL",fr:"fr-FR",it:"it-IT",es:"es-ES",pt:"pt-PT",ru:"ru-RU",uk:"uk-UA",zh:"zh-CN",da:"da-DK",fi:"fi-FI",nb:"nb-NO",ja:"ja-JP",hi:"hi-IN",pl:"pl-PL",cs:"cs-CZ",sv:"sv-SE","pt-br":"pt-BR",hu:"hu-HU",ko:"ko-KR",tr:"tr-TR"}[r]??"en-US"}function ba(s){Jr.decimals=typeof s=="number"&&Number.isInteger(s)&&s>=0&&s<=3?s:0}function xa(){return Jr.decimals??0}function Dt(s){s&&s.currency_decimals!==void 0&&s.currency_decimals!==null&&ba(Number(s.currency_decimals))}function Si(s,r){s&&(ae.date=s.date_format,ae.time=s.time_format,ae.number=s.number_format,r!==void 0&&(ae.country=r||void 0))}function wa(s){switch(ae.number){case"comma_decimal":return["en-US","en"];case"decimal_comma":return["de","es","it"];case"space_comma":return["fr","sv","cs"];case"system":return;default:return Yr(s)}}function I(s,r,e){if(!Number.isFinite(s))return String(s);let t=typeof e=="number"?{minimumFractionDigits:e,maximumFractionDigits:e}:{maximumFractionDigits:2,...e};if(ae.number==="none"){let i=Math.pow(10,t.maximumFractionDigits??2);return String(Math.round(s*i)/i)}try{return new Intl.NumberFormat(wa(r),t).format(s)}catch{return new Intl.NumberFormat(void 0,t).format(s)}}function Q(s,r,e,t){let i=t??xa(),a=Math.abs(s)<.5/Math.pow(10,i)?0:s,o=I(a,e,i);return r?`${o} ${r}`:o}function Ue(s,r,e){let t=I(s,e);return r?`${t} ${r}`:t}function ze(s){let r=Yr(s),e=ae.country;if(e&&/^[A-Za-z]{2}$/.test(e)){let t=`${_e(s).split("-")[0]}-${e.toUpperCase()}`;try{return new Intl.DateTimeFormat(t),t}catch{}}return r}function Zr(s,r){let e=String(s.getDate()).padStart(2,"0"),t=String(s.getMonth()+1).padStart(2,"0"),i=String(s.getFullYear());switch(ae.date){case"DMY":return`${e}/${t}/${i}`;case"MDY":return`${t}/${e}/${i}`;case"YMD":return`${i}-${t}-${e}`;case"system":return s.toLocaleDateString(void 0,{day:"2-digit",month:"2-digit",year:"numeric"});default:return s.toLocaleDateString(ze(r),{day:"2-digit",month:"2-digit",year:"numeric"})}}function Mt(s,r){switch(ae.time){case"12":return s.toLocaleTimeString(ze(r),{hour:"2-digit",minute:"2-digit",hour12:!0});case"24":return s.toLocaleTimeString(ze(r),{hour:"2-digit",minute:"2-digit",hour12:!1});case"system":return s.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"});default:return s.toLocaleTimeString(ze(r),{hour:"2-digit",minute:"2-digit"})}}function F(s,r){if(!s)return"\u2014";try{let e=s.includes("T")?s:s+"T00:00:00";return Zr(new Date(e),r)}catch{return s}}function zt(s,r){if(!s)return"\u2014";try{let e=new Date(s);return Zr(e,r)+" "+Mt(e,r)}catch{return s}}function Xr(s,r){if(s==null)return"\u2014";let e=r||"en";return s<0?`${Math.abs(s)} ${n("d_overdue",e)}`:s===0?n("today",e):`${s} ${s===1?n("day",e):n("days",e)}`}function Ae(s,r,e){return s==null?"\u2014":`${s} ${n("unit_"+(r||"days"),e)}`}function Ft(s,r){return s==null?"\u2014":`${I(s,r,{maximumFractionDigits:1})} ${n("minutes_short",r)}`}function Fe(s,r,e="long"){return new Date(Date.UTC(2024,0,1+s)).toLocaleDateString(ze(r),{weekday:e,timeZone:"UTC"})}function Ti(s,r,e="long"){return new Date(Date.UTC(2024,s,1)).toLocaleDateString(ze(r),{month:e,timeZone:"UTC"})}function es(s,r,e="long"){return Fe((s.getDay()+6)%7,r,e)}function ts(s,r,e="long"){return Ti(s.getMonth(),r,e)}function is(s,r){let e=s.schedule;if(s.schedule_type==="sensor_based"){let i=n("sensor_based",r),a=e&&e.kind==="interval"&&e.every?e.every:s.interval_days;return a?`${i} \xB7 ${Ae(a,(e&&e.kind==="interval"?e.unit:s.interval_unit)||"days",r)}`:i}let t=e?.offset?` ${e.offset>0?"+":"\u2212"}${Math.abs(e.offset)}d`:"";switch(e?.kind){case"weekdays":return((e.weekdays||[]).map(i=>Fe(i,r,"short")).join(" & ")||"\u2014")+t;case"nth_weekday":return e.weekday==null||e.nth==null?"\u2014":`${e.nth===-1?n("ord_last",r):n("ord_"+e.nth,r)} ${Fe(e.weekday,r,"long")}${t}`;case"day_of_month":return e.day==null?"\u2014":(e.day===-1?n(e.business?"last_business_day_month":"last_day_month",r):`${n("day_word",r)} ${e.day}`)+t;case"calendar":return`${n("calendar_entity_label",r)}: ${s.schedule_entity_name||e.entity_id||"\u2014"}${t}`;case"one_time":return s.due_date?F(s.due_date,r):n("one_time",r);case"manual":return n("manual",r);case"interval":return Ae(e.every,e.unit,r)}return s.schedule_type==="one_time"?s.due_date?F(s.due_date,r):n("one_time",r):s.schedule_type==="manual"?n("manual",r):s.schedule_type==="sensor_based"?n("sensor_based",r):s.interval_days!=null?Ae(s.interval_days,s.interval_unit,r):"\u2014"}function rs(s,r){s.currentTarget.dispatchEvent(new CustomEvent("hass-more-info",{detail:{entityId:r},bubbles:!0,composed:!0}))}var ya,Qr,ae,Jr,Be,ka,$a,Ut,T=y(()=>{"use strict";C();zr();Nt();Ot();Ei();Ot();ya="\u20AC";ki(Fr);Qr=window,ae=Qr.__msDateTimePrefs??={},Jr=Qr.__msMoneyPrefs??={};Be=E` +Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"{field}: too long (max {n} characters)",err_too_short:"{field}: too short (min {n} characters)",err_value_too_high:"{field}: too large (max {n})",err_value_too_low:"{field}: too small (min {n})",err_required:"{field}: required",err_wrong_type:"{field}: wrong type (expected: {type})",err_invalid_choice:"{field}: not an allowed value",err_invalid_value:"{field}: invalid value",ws_err_not_found:"Not found \u2014 it may have been deleted meanwhile",ws_err_invalid_input:"Invalid input",ws_err_invalid_date:"Invalid date",ws_err_invalid_format:"Invalid format",ws_err_invalid_url:"Invalid link",ws_err_invalid_mirror_todo:"Invalid to-do list",ws_err_not_configured:"Not set up yet",ws_err_not_loaded:"Not loaded yet \u2014 try again in a moment",ws_err_limit_reached:"Limit reached",ws_err_too_many:"Too many entries",ws_err_too_large:"Too large",ws_err_not_archived:"Not archived",ws_err_invalid_target:"Invalid target",ws_err_create_failed:"Could not be created",ws_err_archived:"Archived \u2014 restore it first",ws_err_already_archived:"Already archived",ws_err_unavailable:"Currently unavailable",ws_err_unauthorized:"Not permitted for your user",ws_err_too_many_views:"Too many saved views",ws_err_storage_unavailable:"Storage is not available",ws_err_replace_failed:"Replacement failed",ws_err_not_paused:"Not paused",ws_err_not_available:"Nothing available",ws_err_no_url:"No link given",ws_err_no_phases:"This task has no phases",ws_err_invalid_view:"Invalid view",ws_err_invalid_user:"Unknown user",ws_err_invalid_range:"Invalid range",ws_err_invalid_parent:"Invalid parent object",ws_err_invalid_device:"Unknown device",ws_err_empty_csv:"The CSV file is empty",ws_err_empty:"Nothing to import",ws_err_duplicate_failed:"Could not duplicate",ws_err_already_paused:"Already paused",ws_err_invalid_cursor:"Invalid phase position",ws_err_invalid_entity_slug:"Invalid entity name",ws_err_invalid_trigger_config:"Invalid trigger configuration",ws_err_no_defaults:"No quick-complete defaults set",ws_err_self_link_device:"An object cannot be linked to itself",ws_err_too_early:"Too early \u2014 this task can only be completed closer to its due date",ws_err_invalid_search_template:"The search link must be an http(s) URL containing {q}",ws_err_invalid_icon:"The notification icon must be an mdi: name, e.g. mdi:air-filter",feat_schedule_time:"Time-of-day scheduling",feat_schedule_time_desc:"Tasks become overdue at a specific time of day instead of midnight.",schedule_time_toggle:"Due at a specific time",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",notes_markdown_hint:"Markdown is supported \u2014 **bold**, lists, [links](\u2026)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",completed_at_optional:"Completed at (optional, empty = now)",completed_at_pick:"Set date & time",completed_at_future_error:"The completion date cannot be in the future.",ha_time_zone_hint:"Time in Home Assistant's time zone ({zone})",days:"days",day:"day",today:"Today",d_overdue:"d overdue",no_tasks:"No maintenance tasks yet. Create an object to get started.",no_tasks_short:"No tasks",no_history:"No history entries yet.",show_all:"Show all",cost_duration_chart:"Cost & Duration",installed:"Installed",min:"Min",max:"Max",save:"Save",saving:"Saving\u2026",edit_task:"Edit Task",new_task:"New Maintenance Task",task_name:"Task name",maintenance_type:"Maintenance type",priority:"Priority",labels:"Labels",labels_placeholder:"e.g. safety, seasonal, tenant-visible",labels_help:"Comma-separated tags for filtering and reporting.",task_mirror_todo:"Mirror into to-do lists",task_mirror_todo_hint:"The task appears in these lists while it is due; checking it off there completes it here.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",all_priorities:"All priorities",schedule_type:"Schedule type",interval_days:"Interval (days)",warning_days:"Warning days",earliest_completion_days:"Earliest completion (days before due)",earliest_completion_days_help:"Leave empty to allow completing any time. 0 = only on/after the due date.",last_performed_optional:"Last performed (optional)",interval_anchor:"Interval anchor",anchor_completion:"From completion date",anchor_planned:"From planned date (no drift)",edit_object:"Edit Object",name:"Name",manufacturer_optional:"Manufacturer (optional)",model_optional:"Model (optional)",serial_number_optional:"Serial number (optional)",serial_number_label:"S/N",object_replaces:"Replaces",object_replaced_by:"Replaced by",documentation_url_label:"Manual",object_notes_label:"Notes",sort_due_date:"Due date",sort_object:"Object name",sort_type:"Type",sort_task_name:"Task name",all_objects:"All objects",all_parts:"All parts",all_areas:"All areas",areas_filter:"Filter areas\u2026",areas_filter_none:"No area matches the filter.",area_cost_year:"Cost {year}",area_object_count:"{n} objects",area_object_count_one:"1 object",area_range_12m:"Last 12 months",area_range_all:"All time",area_task_filter:"Filter by task\u2026",area_kpi_completions:"Completions",area_kpi_total_time:"Total time",area_cost_per_month:"Cost per month",area_cost_per_year:"Cost per year",area_cost_per_object:"Cost per object",area_cost_share:"Share",area_history_section:"History (all objects)",area_report_print:"Area report (PDF)",area_report_title:"Area report",area_report_period:"Period",area_no_costs:"No costs recorded in this period.",no_tasks_yet:"No tasks yet",add_first_task:"Add first task",trigger_configuration:"Trigger Configuration",entity_id:"Entity ID",comma_separated:"comma-separated",entity_logic:"Entity logic",entity_logic_any:"Any entity triggers",entity_logic_all:"All entities must trigger",entities:"entities",attribute_optional:"Attribute (optional, blank = state)",use_entity_state:"Use entity state (no attribute)",trigger_above:"Trigger above",trigger_below:"Trigger below",trigger_equals:"Trigger when equal to (=)",trigger_not_equals:"Trigger when different from (\u2260)",for_at_least_minutes:"For at least (minutes)",safety_interval:"Safety interval (optional)",trigger_combinator:"Combine trigger and interval",trigger_combinator_any:"Trigger or interval (whichever first)",trigger_combinator_all:"Trigger and interval (both required)",delta_mode:"Delta mode",from_state_optional:"From state (optional)",to_state_optional:"To state (optional)",documentation_url_optional:"Documentation URL (optional)",object_notes_optional:"Notes (optional)",nfc_tag_id_optional:"NFC Tag ID (optional)",nfc_tags_empty_help:"No NFC tags registered in Home Assistant yet.",nfc_tags_open_settings:"Open Tags settings",nfc_tags_refresh:"Refresh",environmental_entity_optional:"Environmental sensor (optional)",environmental_entity_helper:"e.g. sensor.outdoor_temperature \u2014 adjusts the interval based on environmental conditions",adaptive_prediction_enabled:"Enable sensor-driven predictions",adaptive_seasonal_enabled:"Enable seasonal awareness",adaptive_max_interval:"Maximum interval (days)",adaptive_min_interval:"Minimum interval (days)",adaptive_ewa_alpha:"Learning rate (alpha)",adaptive_enabled:"Enable adaptive scheduling",adaptive_section_title:"Adaptive Scheduling",environmental_attribute_optional:"Environmental attribute (optional)",nfc_tag_id:"NFC Tag ID",nfc_linked:"NFC tag linked",nfc_link_hint:"Click to link NFC tag",responsible_user:"Responsible User",shared_with:"Shared with (rotation)",shared_with_help:"Pick multiple people to share this task; the responsible person rotates on each completion.",rotation_strategy:"Rotation",rotation_none:"No rotation",rotation_round_robin:"Round-robin",rotation_least_completed:"Least completed",rotation_random:"Random",no_user_assigned:"(No user assigned)",all_users:"All Users",my_tasks:"My Tasks",tab_calendar:"Calendar",cal_no_events:"No maintenance",cal_every_n_days:"every {n} days",cal_every_day:"every day",cal_source_time:"Time-based",cal_source_time_adaptive:"Time-based (adaptive)",cal_source_sensor:"Sensor-based",cal_predicted:"predicted",cal_confidence_high:"high confidence",cal_confidence_medium:"medium confidence",cal_confidence_low:"low confidence",budget_monthly:"Monthly budget",budget_yearly:"Yearly budget",groups:"Groups",new_group:"New group",edit_group:"Edit group",no_groups:"No groups yet",delete_group:"Delete group",delete_group_confirm:"Delete group '{name}'?",group_select_tasks:"Select tasks",group_name_required:"Name is required",description_optional:"Description (optional)",selected:"Selected",loading_chart:"Loading chart data...",hide_outliers:"Hide outliers (sensor glitches)",was_maintenance_needed:"Was this maintenance needed?",feedback_needed:"Needed",feedback_not_needed:"Not needed",feedback_not_sure:"Not sure",suggested_interval:"Suggested interval",apply_suggestion:"Apply",reanalyze:"Re-analyze",reanalyze_result:"New analysis",reanalyze_insufficient_data:"Not enough data to produce a recommendation",data_points:"data points",dismiss_suggestion:"Dismiss",confidence_low:"Low",confidence_medium:"Medium",confidence_high:"High",recommended:"recommended",seasonal_awareness:"Seasonal Awareness",edit_seasonal_overrides:"Edit seasonal factors",seasonal_overrides_title:"Seasonal factors (override)",seasonal_overrides_hint:"Factor per month (0.1\u20135.0). Empty = learned automatically.",seasonal_override_invalid:"Invalid value",seasonal_override_range:"Factor must be between 0.1 and 5.0",clear_all:"Clear all",clear:"Clear",date_type_toggle:"Type the date",date_type_invalid:"Not a valid date \u2014 try 1978-03-15, a year like 1978, or your own date format",seasonal_chart_title:"Seasonal Factors",seasonal_learned:"Learned",seasonal_manual:"Manual",month_jan:"Jan",month_feb:"Feb",month_mar:"Mar",month_apr:"Apr",month_may:"May",month_jun:"Jun",month_jul:"Jul",month_aug:"Aug",month_sep:"Sep",month_oct:"Oct",month_nov:"Nov",month_dec:"Dec",sensor_prediction:"Sensor Prediction",degradation_trend:"Trend",trend_rising:"Rising",trend_falling:"Falling",trend_stable:"Stable",trend_insufficient_data:"Insufficient data",days_until_threshold:"Days until threshold",threshold_exceeded:"Threshold exceeded",environmental_adjustment:"Environmental factor",sensor_prediction_urgency:"Sensor predicts threshold in ~{days} days",day_short:"day",weibull_reliability_curve:"Reliability Curve",weibull_failure_probability:"Failure Probability",weibull_r_squared:"Fit R\xB2",beta_early_failures:"Early Failures",beta_random_failures:"Random Failures",beta_wear_out:"Wear-out",beta_highly_predictable:"Highly Predictable",confidence_interval:"Confidence Interval",confidence_conservative:"Conservative",confidence_aggressive:"Optimistic",current_interval_marker:"Current interval",recommended_marker:"Recommended",characteristic_life:"Characteristic life",chart_mini_sparkline:"Trend sparkline",chart_history:"Cost and duration history",chart_seasonal:"Seasonal factors, 12 months",chart_weibull:"Weibull reliability curve",chart_sparkline:"Sensor trigger value chart",days_progress:"Days progress",qr_code:"QR Code",qr_generating:"Generating QR code\u2026",qr_error:"Failed to generate QR code.",qr_error_no_url:"No HA URL configured. Please set an external or internal URL in Settings \u2192 System \u2192 Network.",save_error:"Failed to save. Please try again.",subsave_warning:"Saved, but one setting was rejected: {detail}",qr_print:"Print",qr_download:"Download SVG",qr_action_view:"View maintenance info",qr_action_complete:"Mark maintenance as complete",qr_action_quick_complete:"Quick-complete \u2014 no dialog",qr_print_quick_none:"Quick-complete codes need quick-complete defaults on a task (Settings \u2192 Advanced Features \u2192 Completion actions; task dialog \u2192 Quick-complete defaults) \u2014 none of the chosen objects' tasks has them yet.",qr_url_mode:"Link type",qr_mode_companion:"Companion App",qr_mode_local:"Local (mDNS)",qr_mode_server:"Server URL",overview:"Overview",strat_this_month:"This Month",strat_later:"Later",strat_week:"Week",strat_fortnight:"Fortnight",strat_month:"Month",strat_year:"Year",strat_other:"Other",strat_status:"Status",strat_empty_title:"No maintenance objects yet",strat_empty_content:"Open the Maintenance panel to add your first object \u2014 pool pump, HVAC filter, vehicle, anything that needs scheduled care.",strat_open_panel:"Open Maintenance panel",strat_add_object:"Add object",strat_not_loaded:"**Maintenance Supporter** is not loaded. Install/enable the integration first.",strat_group_by:"Group views by",strat_group_area:"By area (default)",strat_group_status:"By status (Overdue / Triggered / Due Soon / OK)",strat_group_floor:"By floor (uses HA floors)",strat_group_due_date:"By due date (Overdue / Today / Week / Month / Later)",strat_group_calendar:"Rolling calendar (Week / Fortnight / Month / Year)",strat_editor_help:'The "Overview" view is always first. Empty groups are skipped.',picker_name_calendar:"Maintenance Supporter \u2014 Calendar",picker_name_panel:"Maintenance Supporter \u2014 Panel",picker_name_budget:"Maintenance Supporter \u2014 Budget",picker_name_groups:"Maintenance Supporter \u2014 Groups",picker_name_vacation:"Maintenance Supporter \u2014 Vacation",picker_name_battery_fleet:"Battery Fleet",picker_name_section:"Maintenance Supporter \u2014 Section",picker_name_vacation_status:"Maintenance Supporter \u2014 Vacation Status",picker_name_budget_status:"Maintenance Supporter \u2014 Budget Status",picker_desc_card:"Overview of your maintenance tasks with quick actions.",picker_desc_calendar:"Rolling calendar of maintenance tasks with 7/14/30/365 day windows, source icons, and prediction-confidence pills.",picker_desc_panel:"The complete Maintenance Supporter panel as a card \u2014 for a panel view / dashboard subview without the sidebar entry.",picker_desc_budget:"Edit the monthly and yearly budget right on the dashboard.",picker_desc_groups:"Create, rename and delete task groups right on the dashboard.",picker_desc_vacation:"Switch vacation mode on or off and set its dates right on the dashboard.",picker_desc_battery_fleet:"All tracked batteries: what is low now, what runs out soon, and what to buy.",picker_desc_dashboard:"Auto-generated dashboard. Group views by area, status, floor, or due date \u2014 picked from the strategy editor or YAML.",picker_desc_section:"Embed maintenance tasks (filterable by area, status, due date) as a section in any dashboard view.",picker_desc_vacation_status:"Compact vacation-mode status banner. Tap to open settings in the panel.",picker_desc_budget_status:"Monthly + yearly maintenance budget overview. Tap for details.",picker_desc_groups_status:"List of configured task groups with member counts.",hist_note_button:"Completed from dashboard button",hist_note_todo:"Completed from the To-do list",hist_note_voice:"Completed by voice",hist_note_notification:"Completed from the notification",hist_note_nfc:"Completed via NFC tag",hist_note_shopping_list:"Completed from the shopping list",hist_note_todo_mirror:"Completed from a mirrored to-do list",hist_note_skipped_notification:"Skipped from notification",hist_note_skipped_button:"Skipped from dashboard button",hist_note_sensor_triggered:"Sensor trigger activated",hist_note_initial:"Initial value set during task creation",hist_note_auto_recovered:"Auto-completed: sensor recovered ({value})",hist_note_reset:"Reset to {date}",hist_note_trigger_replaced:"Trigger entity replaced: {old} \u2192 {new}",hist_note_trigger_removed:"Sensor trigger removed (entity was: {entity}). Schedule converted to {schedule}.",hist_note_compound_removed:"Entity {entity} removed from compound trigger; {count} conditions remain.",recent_activities:"Recent Activities",search_notes:"Search notes",avg_cost:"Avg Cost",current:"Current",shorter:"Shorter",longer:"Longer",normal:"Normal",disabled:"Disabled",compound_logic:"Compound logic",compound:"Compound (multiple conditions)",compound_logic_and:"AND \u2014 all conditions must trigger",compound_logic_or:"OR \u2014 any condition triggers",compound_help:"Combine several sensor conditions into one trigger.",compound_no_conditions:"No conditions yet \u2014 add at least one.",compound_add_condition:"Add condition",compound_condition:"Condition",compound_remove_condition:"Remove condition",card_title:"Title",card_show_header:"Show header with statistics",card_show_actions:"Show action buttons",card_action_style:"Complete button style",card_compact:"Compact mode",card_max_items:"Max items (0 = all)",card_filter_status:"Filter by status",card_filter_status_help:"Empty = show all statuses.",card_filter_objects:"Filter by objects",card_filter_objects_help:"Empty = show all objects.",card_filter_areas:"Filter by areas",card_filter_areas_help:"Empty = show all areas.",card_filter_priority_help:"Empty = show all priorities. Tasks without an explicit priority count as Normal.",card_filter_entities:"Filter by entities (entity_ids)",card_filter_entities_help:"Pick sensor / binary_sensor entities from this integration. Empty = all.",card_loading_objects:"Loading objects\u2026",card_load_error:"Could not load objects \u2014 check the WebSocket connection.",card_no_tasks_title:"No maintenance tasks yet",card_no_tasks_cta:"\u2192 Create one in the Maintenance panel",no_objects:"No objects yet.",action_error:"Action failed. Please try again.",area_id_optional:"Area (optional)",installation_date_optional:"Installation date (optional)",warranty_expiry_optional:"Warranty expiry (optional)",warranty:"Warranty",warranty_valid_until:"valid until {date}",warranty_expires_in:"expires in {days} days",warranty_expired:"expired",cal_past_windows:"Past windows",cal_forward_windows:"Forward windows",history_edit_title:"Edit history entry",history_delete_entry:"Delete entry",history_delete_confirm_entry:"Delete this history entry? The task's last-performed date falls back to the previous completion; photos stay with the object.",history_delete_returns_parts:"The parts it used go back to stock: {parts}.",history_edit_timestamp:"Timestamp",manufacturer:"Manufacturer",model:"Model",area:"Area",actions:"Actions",view_mode_label:"View",view_cards:"Card view",view_table:"Table view",objects_table_columns_label:"Objects table columns",objects_table_columns_hint:"Choose which columns appear in the objects table view.",custom_icon_optional:"Icon (optional, e.g. mdi:wrench)",notify_icon:"Notification icon",notify_icon_hint:"Shown by the Companion app on Android. Empty = the icon of this task's maintenance type ({default}).",task_enabled:"Task enabled",skip_reason_prompt:"Skip this task?",reason_optional:"Reason (optional)",reset_date_prompt:"Mark task as performed?",reset_date_optional:"Last performed date (optional, defaults to today)",notes_label:"Notes",documentation_label:"Documentation",no_nfc_tag:"\u2014 No tag \u2014",dashboard:"Dashboard",tab_today:"Today",palette_placeholder:"Search objects, tasks, spare parts, documents, notes\u2026",palette_no_results:"No matches",palette_hint:"\u2191\u2193 to navigate \xB7 Enter to open \xB7 Esc to close \xB7 / opens the search anywhere",search_open:"Search",search_group_parts:"Spare parts",search_group_content:"In documents",search_group_history:"History notes",search_page:"Page {page}",search_empty_hint:"Type to search objects, tasks, spare parts, documents and notes",search_searching:"Searching documents and notes\u2026",search_index_status:"Full-text search: {indexed} of {total} files indexed \xB7 {no_text} without text layer \xB7 {pending} pending",today_all_caught_up:"All caught up! Nothing due this week.",today_overdue:"Overdue",today_due_today:"Due today",today_this_week:"This week",settings:"Settings",settings_features:"Advanced Features",settings_features_desc:"Enable or disable advanced features. Disabling hides them from the UI but does not delete data.",feat_adaptive:"Adaptive Scheduling",feat_adaptive_desc:"Learn optimal intervals from maintenance history",feat_seasonal:"Seasonal Adjustments",feat_seasonal_desc:"Adjust intervals based on seasonal patterns",feat_environmental:"Environmental Correlation",feat_environmental_desc:"Correlate intervals with temperature/humidity",feat_budget:"Budget Tracking",feat_budget_desc:"Track monthly and yearly maintenance spending",feat_groups:"Task Groups",feat_groups_desc:"Organize tasks into logical groups",feat_checklists:"Checklists",feat_checklists_desc:"Multi-step procedures for task completion",settings_general:"General",settings_default_warning:"Default warning days",settings_consumable_threshold:"Consumable low threshold (%)",settings_battery_low_percent:"Battery low threshold (%)",settings_battery_recovered_percent:"Battery counts as replaced above (%)",settings_battery_recovered_percent_hint:"A low battery stays counted as low until its level rises above this value or a replacement is recorded \u2014 so a level hovering around the low threshold cannot complete the fleet task again and again.",settings_battery_auto_record:"Record a replacement automatically when a low battery recovers",settings_battery_auto_record_hint:"When a battery that was low rises above the recovery threshold, the replacement date is recorded in Battery Notes and the spare cells are taken from stock \u2014 no tap needed.",settings_battery_lifetimes:"Typical battery lifetimes",settings_battery_lifetimes_hint:'Only used for batteries without a percentage: a Battery Notes note that has just a type and a last-replaced date, or a sensor that only reports "low". Their due date is last replaced + this lifetime. Batteries that report a level get their forecast from the measured discharge instead. Once the fleet has seen three replacements of a type, the median interval takes over from the built-in value \u2014 your own value always wins.',settings_battery_lifetime_months:"months",settings_battery_lifetime_reset:"Use default",settings_battery_lifetime_in_fleet:"in your fleet",settings_battery_lifetime_learned_models:"Learned per model: {list}",settings_thresholds_hint:"Defaults for new Suggested setups (percent-remaining consumables) and for fleet batteries without their own Battery Notes threshold. Existing tasks keep their value.",settings_part_search_url:"Shopping search link",settings_part_search_url_hint:"URL with {q} in place of the search terms, used for parts without a product link. Leave empty for the automatic store (by country, then by language).",settings_value_out_of_range:"Value must be between {min} and {max}",bn_summary:"{name}: {pct} % for {n} batteries",bn_floor_decides:"Your {floor} % floor decides for all of them; the higher of the two thresholds counts per battery.",bn_above_floor:"That is above your {floor} % floor, so {name} decides for all noted batteries.",bn_overrides:"{n} devices with their own threshold:",bn_more:"+ {n} more",settings_row_actions:"Task row actions",settings_ref_numbers_in_lists:"Reference numbers in lists",settings_ref_numbers_in_lists_hint:"Show the #8 / #8.3 chips in front of object and task names in the Today list, the task table, the object cards and the objects table.",settings_compact_refs:"Compact reference numbers",settings_compact_refs_hint:"Renumbers all objects, tasks and completions in order (1, 2, 3 \u2026) and closes the gaps left by deletions. Numbers already printed on service booklets, photos or notes will no longer match.",settings_compact_refs_confirm:"Renumber every object, task and completion sequentially in creation order? Numbers already written on printed service booklets, photos or notes will no longer match. This cannot be undone.",settings_compact_refs_done:"Renumbered {objects} objects, {tasks} tasks and {completions} completions.",row_actions_buttons_compact:"Buttons (icons only on phones)",row_actions_buttons:"Buttons with text",row_actions_icons:"Icons (classic)",row_actions_follow:"Follow the household setting",settings_panel_enabled:"Sidebar panel",settings_panel_title:"Sidebar panel title",settings_notifications:"Notifications",settings_notify_service:"Notification service",settings_shopping_list:"Shopping list (buy tasks)",settings_shopping_list_help:"Low-part buy reminders appear in this to-do list; checking one off restocks the part.",shopping_list_none:"Off \u2014 no shopping list",settings_install_assist_sentences:"Install Assist sentences",settings_install_assist_sentences_hint:"Copies the voice sentences into your configuration so the classic Assist agent recognises them. A file you edited yourself is never overwritten.",test_notification:"Test notification",send_test:"Send test",testing:"Sending\u2026",test_notification_success:"Test notification sent",test_notification_failed:"Test notification failed",notify_per_person:"Per-person delivery",member_avatars:"Member avatars",member_avatars_hint:"Shown next to assigned tasks \u2014 initials in the member's colour. Defaults come from the name; set your own initials or pick a colour to tell members apart.",member_initials:"Initials",member_color:"Colour",member_avatar_reset:"Reset",notify_no_own_device:"No own device \u2014 uses the household service",settings_notify_due_soon:"Notify when due soon",settings_notify_overdue:"Notify when overdue",settings_notify_triggered:"Notify when triggered",settings_interval_hours:"Repeat interval (hours, 0 = once)",settings_quiet_hours:"Quiet hours",settings_quiet_start:"Start",settings_quiet_end:"End",settings_max_per_day:"Max notifications per day (0 = unlimited)",settings_bundling:"Bundle notifications",settings_bundle_threshold:"Bundle threshold",settings_reminder_leads:"Extra reminders (days before due)",settings_reminder_leads_hint:"Comma-separated lead times, e.g. 14, 3, 0 \u2014 one extra reminder fires on each matching day. Empty = off.",settings_actions:"Mobile Action Buttons",settings_action_complete:"Show 'Complete' button",settings_action_skip:"Show 'Skip' button",settings_action_snooze:"Show 'Snooze' button",settings_weekly_digest:"Weekly digest",settings_weekly_digest_hint:"A single summary notification on Monday morning when tasks are due.",settings_warranty_reminder:"Warranty expiry reminder",settings_warranty_reminder_days:"Days before expiry",settings_warranty_reminder_hint:"Notify once when an object's warranty is this many days from expiring.",settings_snooze_hours:"Snooze duration (hours)",settings_snooze_hours_hint:"How long Snooze mutes a task's reminders \u2014 from the task menu, a card, voice or the phone's Snooze button. The due date stays as it is.",settings_budget:"Budget",settings_currency:"Currency",settings_currency_decimals:"Decimal places for amounts",settings_currency_decimals_hint:"How many decimals every amount shows: KPIs, budgets, costs in lists and reports, budget alerts. 0 = whole numbers.",settings_parts_cost_mode:"Spare parts count as spending",settings_parts_cost_purchase:"When bought",settings_parts_cost_use:"When used",settings_parts_cost_mode_hint:"When bought: a buy reminder's cost counts, and the parts a job uses are shown as information. When used: every completion books the value of the parts it used, and a purchase sets the part's price instead of counting. Applies to completions from now on \u2014 history keeps how it was booked.",settings_budget_monthly:"Monthly budget",settings_budget_yearly:"Yearly budget",settings_budget_alerts:"Budget alerts",settings_budget_threshold:"Alert threshold (%)",settings_import_export:"Import / Export",settings_export_json:"Export JSON",settings_export_yaml:"Export YAML",settings_export_csv:"Export CSV",settings_export_settings:"Export settings (JSON)",settings_import_placeholder:"Paste JSON or CSV content here\u2026",settings_import_btn:"Import",settings_import_success:"{count} objects imported successfully.",settings_import_unmatched_users:"Not found on this instance: {names}. Their task assignments were removed \u2014 add people with the same names in Home Assistant and import again to keep them.",settings_export_success:"Export downloaded.",settings_saved:"Setting saved.",settings_include_history:"Include history",settings_export_selection:"Limit to selected objects (optional)",settings_docs_archive:"Documents archive (with files)",settings_docs_archive_hint:"The JSON and YAML exports list your documents but not their files; CSV leaves documents out. This ZIP holds the uploaded files \u2014 restore it after importing the JSON and nothing is missing.",settings_docs_export_btn:"Download documents ZIP",settings_docs_import_btn:"Restore documents ZIP",settings_docs_import_success:"Restored: {blobs} files, {docs} documents",settings_docs_import_missing:"{n} skipped \u2014 their files were not in the ZIP",sort_alphabetical:"Alphabetical",sort_due_soonest:"Due soonest",sort_task_count:"Task count",sort_area:"Area",sort_assigned_user:"Assigned user",sort_group:"Group",groupby_none:"No grouping",groupby_area:"By area",groupby_group:"By group",groupby_user:"By user",groupby_object:"By object",filter_label:"Filter",user_label:"User",photo_label:"Photo",sort_label:"Sort",group_by_label:"Group by",state_value_help:'Use the HA state value (usually lowercase, e.g. "on"/"off"). Case is normalised on save.',target_changes_help:"Number of matching transitions before the trigger fires (default: 1).",state_latch_help:"With 1 transition the trigger is a latch: it recovers when the entity leaves the To-state \u2014 or, with only a From-state set, when it returns to that state.",for_minutes_state_help:"0 counts every change immediately. Set minutes and the new state must hold that long first \u2014 brief flickers then neither trigger nor count.",qr_print_title:"Print QR codes",qr_print_desc:"Generate a printable page of QR codes to cut out and stick on your equipment.",qr_print_load:"Load objects",qr_print_filter:"Filter",qr_print_objects:"Objects",qr_print_actions:"Actions",qr_print_url_mode:"Link type",qr_print_estimate:"Estimated QR codes",qr_print_over_limit:"cap is 200, narrow the filter",qr_print_generate:"Generate QR codes",qr_print_generating:"Generating\u2026",qr_print_ready:"QR codes ready",qr_print_print_button:"Print",qr_print_empty:"Nothing to generate",qr_action_skip:"Skip",vacation_title:"Vacation mode",vacation_active:"active",vacation_ended:"ended",vacation_desc:"Plan a vacation: notifications are paused during the period plus a buffer of days. You can opt specific tasks back in.",vacation_enable:"Enable vacation mode",vacation_start:"Start",vacation_end:"End",vacation_buffer:"Buffer (days)",vacation_exempt_title:"Notify anyway during vacation",vacation_exempt_desc:"Pick tasks that should still notify during vacation (e.g. critical pool chemistry).",vacation_load_tasks:"Load tasks",vacation_preview_btn:"Show preview",vacation_preview_affected:"tasks affected",vacation_event_due_soon:"becomes due soon",vacation_event_overdue:"becomes overdue",vacation_event_triggered_est:"sensor trigger possible",vacation_sensor_based:"(sensor-based)",vacation_action_notify:"Notify anyway",vacation_action_unsilence:"Silence again",vacation_marked_complete:"Marked complete",vacation_marked_skip:"Skipped",vacation_end_now:"End vacation now",add:"Add",show_stats:"Show stats + graphs",hide_stats:"Hide stats",adaptive_no_data:"Not enough completion history yet for adaptive analysis. Complete this task a few more times to unlock interval recommendations and reliability charts.",suggestion_applied:"Suggested interval applied",vacation_mode:"Vacation mode",vacation_status_active:"Active now",vacation_status_scheduled:"Scheduled",vacation_status_inactive:"Inactive",vacation_end_now_confirm:"End vacation immediately?",vacation_exempt_count:"exempt",vacation_advanced:"Advanced\u2026",vacation_open_panel:"Open in panel",enable:"Enable",saved:"Saved",budget_monthly_set:"Set monthly",budget_yearly_set:"Set yearly",budget_advanced:"Currency, alerts\u2026",budget_open_panel:"Open in panel",groups_empty:"No groups yet.",group_new_placeholder:"Add group\u2026",groups_manage_tasks:"Manage task assignments\u2026",groups_open_panel:"Open in panel",unassigned:"Unassigned",no_area:"No area",has_overdue:"Has overdue tasks",object:"Object",settings_panel_access:"Panel access",settings_panel_access_desc:"Admins always have full access. To delegate create, edit and delete to specific non-admins, switch this on and pick them below \u2014 everyone else only gets Complete, Skip, Reset, Postpone and Snooze.",settings_operator_write:"Allow selected users to create, edit & delete",settings_operator_write_desc:"Off: only admins can change content. On: the selected users below get full access too.",no_non_admin_users:"No non-admin users found. Add some in Settings \u2192 People.",owner_label:"Owner",feat_completion_actions:"Completion actions",feat_completion_actions_desc:"Per-task HA action on complete + quick-complete QR with pre-set values.",on_complete_action_title:"On complete: trigger HA action (optional)",on_complete_action_desc:"Calls an HA service when the task is completed \u2014 e.g. reset a counter on the device.",on_complete_action_target:"Target entity",on_complete_action_target_hint:"Note: the entity domain must match the service \u2014 e.g. 'button.press' only works on button.*, 'counter.increment' only on counter.*, 'input_button.press' only on input_button.* etc. On a mismatch the action will silently fail (HA logs 'Referenced entities ... missing or not currently available').",on_complete_action_target_more:"This action targets more than this field shows: {targets}. That is kept as it is \u2014 choosing another entity here replaces the whole target.",on_complete_action_skip_auto:"Not when the task completes itself (its sensor recovered)",on_complete_action_data:"Data (JSON, optional)",on_complete_action_test:"Validate configuration",on_complete_action_test_success:"\u2713 Configuration valid (action will fire only on task completion)",on_complete_action_test_failed:"Failed",action_test_bad_format:"Invalid service format (expected 'domain.service')",action_test_unknown_service:'Service "{service}" is not registered in Home Assistant. Check the spelling and that the integration providing it is loaded.',action_test_domain_mismatch:`Service "{service}" only works on {domain}.* entities; entity "{entity}" is in {entity_domain}.* \u2014 pick a service that matches the entity's domain (e.g. {suggestion})`,action_test_entity_missing:'Target entity "{entity}" was not found in Home Assistant \u2014 it may have been renamed or its integration removed.',quick_complete_defaults_title:"Quick-complete defaults (for QR scans, optional)",quick_complete_defaults_desc:"Pre-set values for quick-complete QR scans. Without these, the QR opens the complete dialog.",quick_complete_defaults_notes:"Notes",quick_complete_defaults_cost:"Cost",quick_complete_defaults_duration:"Duration (minutes)",quick_complete_defaults_feedback_none:"No feedback",quick_complete_defaults_feedback_needed:"Was needed",quick_complete_defaults_feedback_not_needed:"Not needed",quick_complete_success:"Quickly marked complete",show_all_objects:"Show all objects",show_all_tasks:"Clear filter \u2014 show all tasks",filter_to_overdue:"Filter task list to overdue only",filter_to_due_soon:"Filter task list to due-soon only",filter_to_triggered:"Filter task list to triggered only",open_task:"Open task",show_details:"Show history + stats",hide_details:"Hide details",history_empty:"No history yet.",history_edit_button:"Edit entry",total_cost:"Total cost",times_performed:"Performed",older_entries:"older",open_in_panel:"Open in Maintenance panel",skip_reason:"Skip reason (optional)",reset_to_date:"Reset last_performed to",delete_task_confirm:"Delete this task and its history?",delete_object_confirm:"Delete this object and all its tasks?",loading:"Loading\u2026",archive:"Archive",undo:"Undo",task_completed_named:"Completed: {task}",undo_done:"Completion undone",undo_done_action_ran:"Completion undone. Its completion action had already run and cannot be taken back.",undo_nothing:"Nothing to undo. Only your own last completion can be taken back, within 10 minutes.",undo_changed:"Can't undo. The task has changed since.",task_archived:"Task archived",object_archived:"Object archived",unarchive:"Unarchive",archived:"Archived",show_archived:"Show archived",hide_archived:"Hide archived",section_collapse:"Collapse section",section_expand:"Expand section",confirm_archive_object:"Archive this object and its tasks? They keep their history and can be unarchived later.",settings_archive:"Archive & Retention",settings_archive_desc:"Retire completed one-off tasks without deleting them. Archived items are hidden and inert but keep their history and cost.",settings_archive_oneoff_days:"Auto-archive completed one-off tasks after (days, 0 = off)",settings_delete_archived_oneoff_days:"Auto-delete archived one-off tasks after (days, 0 = never)",archive_object:"Archive object",unarchive_object:"Unarchive object",documents:"Documents",documents_empty:"No documents yet.",doc_upload:"Upload file",doc_uploading:"Uploading\u2026",doc_add_link:"Add link",doc_link_url:"URL (https://\u2026)",doc_link_title:"Title (optional)",doc_open:"Open",doc_delete_confirm:'Delete "{name}"?',doc_too_large:"File is too large (max 25 MB).",doc_upload_failed:"Upload failed.",docs_archive_too_large:"The archive is too large (max {max}).",docs_archive_no_manifest:"This ZIP has no manifest.json, so the restore cannot tell which document a file belongs to. Keep manifest.json next to the folders when zipping again.",completion_photos_optional:"Completion photos (optional)",completion_photos:"Completion photos",choose_photos:"Choose photos",choose_photo:"Choose photo",photos_android_hint:"The Android app hands over one photo per pick \u2014 each pick is added.",photos_limit:"Up to {max} photos per completion",history_edit_photos_hint:"Removing a photo here keeps the file in the object's documents.",uploading:"Uploading\u2026",remove:"Remove",doc_deduped:"Already stored elsewhere \u2014 shared, no extra space used.",doc_dup_in_object:"This file is already attached to this object.",doc_link_invalid:"Only http/https links are allowed.",doc_cat_manual:"Manual",doc_cat_warranty:"Warranty",doc_cat_invoice:"Invoice",doc_cat_spare_parts:"Spare parts",doc_cat_photo:"Photo",doc_cat_other:"Other",doc_link_badge:"Link",doc_storage_title:"Document storage",doc_storage_saved:"Saved via deduplication",doc_storage_refresh:"Refresh",doc_download:"Download",doc_close:"Close",doc_camera:"Take photo",camera_capture_shoot:"Capture",camera_switch_lens:"Switch camera",camera_switching:"Switching camera\u2026",camera_not_answered:"This camera did not answer. Tap the switch again to try the next one, or use the file picker.",camera_diagnostics:"Details for a bug report",camera_copy:"Copy",camera_copied:"Copied",doc_drop_hint:"Drop files here",doc_task_none:"No documents linked to this task.",doc_link_existing:"Link a document\u2026",doc_attach:"Link",doc_unlink:"Unlink",doc_page:"Page",chart_range_7d:"7d",chart_range_30d:"30d",chart_range_90d:"90d",chart_range_1y:"1y",chart_since_service:"since last service",chart_no_stats:"No long-term statistics for this entity \u2014 showing maintenance-event values only",auto_complete_on_recovery:"Auto-complete when the sensor recovers",auto_complete_on_recovery_help:"Records a completion (sets last performed) when the trigger clears itself \u2014 e.g. salt refilled, filter replaced.",doc_search:"Search documents\u2026",doc_search_none:"No matching documents",doc_description:"Description (optional)",doc_sort:"Sort documents",doc_sort_newest:"Newest first",doc_sort_oldest:"Oldest first",doc_sort_title:"By title (1, 2, 3 \u2026)",doc_sort_category:"By category",link_device_optional:"Link to existing device (optional)",parent_object_optional:"Parent object (optional)",parent_none:"(No parent)",paused:"Paused",pause_object:"Pause",resume_object:"Resume",pause_task:"Pause",resume_task:"Resume",pause_task_prompt:"Freeze this task's schedule \u2014 it does not become due and does not notify until you resume it, and resuming starts a fresh cycle from that day. The object's other tasks keep running. Optionally set a date to resume by itself.",task_paused:"Task paused",task_resumed:"Task resumed \u2014 its schedule restarted",pause_until_prompt:"Freeze this object's schedules \u2014 nothing becomes due and nothing notifies until it is resumed. Optionally set an auto-resume date.",pause_until_label:"Resume on (optional)",object_paused:"Object paused",object_resumed:"Object resumed \u2014 schedules restarted",object_paused_badge:"Paused",paused_until_label:"until",replace_object:"Replace\u2026",replace_object_prompt:"Retire this object and create a successor. History and costs stay archived on the old one; tasks and documents carry over to the new one, counters start fresh.",replace_name_label:"Successor name",replace_device_heading:"Device in Home Assistant",replace_device_keep:"Same device as before ({device})",replace_device_keep_hint:"Also the choice when the new unit is not in Home Assistant yet: once it is, pick it as the object's device in its settings \u2014 the sensor triggers move along then.",replace_device_other:"The new unit is another device",replace_device_pick:"Device of the new unit",replace_device_other_hint:"Sensor triggers, completion actions and the counter reset move to the matching entities of that device.",replace_device_none:"No device",object_replaced:"Object replaced \u2014 successor created",device_swap_moved:"{count} sensor links moved to the new device.",device_swap_moved_one:"One sensor link moved to the new device.",device_swap_unmatched:"{count} could not be matched \u2014 check those tasks' sensor triggers.",device_swap_unmatched_one:"One could not be matched \u2014 check that task's sensor trigger.",reading_unit_label:"Reading unit (e.g. kWh, m\xB3)",reading_unit_help:"Shown next to the recorded value when completing this task.",reading_value_label:"Reading value",reading_label:"Reading",readings_section:"Readings",readings_hint:"Several named values per completion \u2014 one row per meter. Leave empty to record a single value.",reading_name_label:"Reading name",reading_unit_short:"Unit",reading_add:"Add reading",reading_duplicate_name:"Name already used",reading_last:"last: {value}",reading_below_last:"Lower than the last reading ({value})",settings_templates_label:"Template gallery",settings_templates_hint:`Untick templates you'll never need \u2014 they disappear from the "From template" pickers (panel and config flow). Nothing else changes; you can re-enable them any time.`,home_profile_title:"Home profile",home_profile_hint:"Recommends templates and sets seasonal windows for your climate and hemisphere. Everything is worked out locally from your floors, areas and home location.",home_type_label:"Home type",home_type_auto:"Automatic ({detected})",home_region_label:"State, province or region",home_region_none:"not found",home_region_hint:"Some rules differ within a country \u2014 a yearly vehicle inspection in one state, none in the next. Found from your home location.",home_dwelling_house:"House",home_dwelling_apartment:"Apartment",home_dwelling_unknown:"not recognised",home_detected_from:"Detected from: {reasons}",home_reason_floors:"several floors",home_reason_basement:"a basement",home_reason_garden:"a garden",home_reason_shed:"a shed",home_reason_driveway:"a driveway",home_reason_garage:"a garage",home_reason_attic:"an attic",home_reason_lawn_mower:"a lawn mower",home_reason_gate:"a gate",home_reason_balcony:"a balcony",home_reason_small_home:"few rooms on one floor",home_reason_ups:"a UPS",home_climate_label:"Climate",home_climate_value:"K\xF6ppen {koppen} \xB7 coldest month {cold} \xB0C \xB7 warmest month {warm} \xB0C",home_climate_unknown:"No home location set in Home Assistant",home_hemisphere_south:"Southern hemisphere \u2014 seasonal windows are mirrored",home_trait_freeze:"Frost in winter",home_trait_snow:"Snowy winters",home_trait_severe_winter:"Severe winters",home_trait_damp:"Damp climate",home_trait_mediterranean:"Mediterranean climate",home_trait_earthquake:"Earthquake region",home_trait_hot_summer:"Hot summers",home_trait_hot_humid:"Hot and humid",home_trait_hot_dry:"Hot and dry",home_trait_termites:"Termite region",home_trait_wildfire:"Wildfire risk",home_trait_cyclone:"Hurricane / typhoon region",home_trait_tropical:"Tropical climate",home_trait_radon:"Radon test recommended",home_reason_starter:"Basics for your home",home_reason_country:"Common in {country}",home_reason_feature:"Found in your home: {feature}",templates_recommended_title:"Recommended for your home",templates_recommended_hint:"Based on your home profile \u2014 adjust it in Settings.",templates_legal_hint:"Notes on legal duties and intervals are guidance, not legal advice. Rules vary by region and change over time \u2014 local regulations and the manufacturer's instructions take precedence.",templates_not_typical:"Not typical for your home type",templates_set_up:"Already set up",worksheet:"Work sheet",worksheet_scan_view:"Scan to open the task",worksheet_scan_complete:"Scan to complete",worksheet_manual_excerpt:"Manual excerpt",worksheet_pages:"pages",worksheet_printed:"Printed",worksheet_never:"Never",card_all_caught_up:"All caught up \u2014 nothing needs attention",postpone:"Postpone",postpone_date_prompt:"Postpone this occurrence to which date?",postpone_date_label:"New due date",postponed:"Postponed",postponed_to:"Postponed to",season_window_label:"Seasonal window (months)",season_window_hint:"Only due in the selected months; off-season dates roll to the next active month. None = all year.",series_end_label:"Ends",series_end_never:"Never (repeats indefinitely)",series_end_after_count:"After a number of times",series_end_until:"On a date",series_end_count_label:"Number of times",series_end_until_label:"End date",parts_section:"Parts & consumables",parts_inventory_value:"Inventory value",part_add:"Add part",part_name:"Name",part_vendor:"Manufacturer",part_storage_location:"Storage location",part_product_url:"Product URL",part_unit:"Unit",part_cost:"Unit price",part_stock:"Stock",part_reorder_threshold:"Reorder at",part_restock_quantity:"Restock quantity",part_package_size:"Package size (units)",part_restock_packages:"Restock amount (packages)",part_package_hint:"Bought in packages, used in the unit: stock, the amount a task uses and the reorder threshold count units; the restock amount and the price are per package.",part_auto_buy:"Auto-create buy task when low",part_restock:"Adjust stock",parts_used_by:"Used by",restock_quantity_label:"Quantity bought",consumes_parts_label:"Consumes parts",task_parts_fleet_hint:"Battery quantities come from Battery Notes per device \u2014 nothing to link here.",shared_parts_other_objects:"Parts from other objects",shared_parts_help:"Several objects can share one stock. Completing this task takes from the owning object.",shared_part_unknown:"Unknown part",parts_load_failed:"Couldn't load this object's parts \u2014 the consumes-parts options are unavailable right now.",adopt_problem_button:"Adopt problem sensors",adopt_problem_title:"Adopt problem sensors",adopt_problem_hint:"Turn HA problem sensors (printer errors, filter warnings, low battery) into maintenance tasks that trigger while the problem is active and clear themselves when it resolves.",adopt_problem_none:"No problem sensors found that aren't already tracked.",adopt_problem_active:"active",adopt_problem_ok:"ok",adopt_problem_new_object:"(new)",adopt_object_hint:"Give several sensors the same object name and they become one object; type an existing object's name to add them there.",adopt_summary_title:"What adopting changes",adopt_summary_existing:'"{name}": tasks {before} \u2192 {after}',adopt_problem_adopt:"Adopt selected",adopt_problem_done:"Adopted {tasks} problem sensor(s)",adopt_problem_done_one:"Adopted 1 problem sensor",views_label:"Views",views_none:"\u2014 No view \u2014",views_manage:"Save / manage views",views_dialog_title:"Saved views",views_dialog_hint:"Save the current filters as a named view everyone can reuse.",views_name_placeholder:"View name",views_save_current:"Save current filters",views_none_yet:"No saved views yet.",close:"Close",trigger_hint_now:"The sensor reads {value} right now.",trigger_hint_above:"The task triggers once it rises above {target}.",trigger_hint_below:"It triggers once it falls below {target}.",trigger_hint_overlap:"These limits overlap: every reading triggers the task and it can never recover. Leave one of them empty.",trigger_hint_counter_delta:"Counts from the current reading ({value}): due at {due} (+{target}), and the count restarts after each completion.",trigger_hint_counter_delta_edit:"Counts usage since the last completion: due after +{target}; the count restarts after each completion.",trigger_hint_counter_abs:"The task becomes due once the sensor reaches {target}.",trigger_hint_runtime:"The task becomes due after {hours} h of accumulated on-time; the counter restarts after each completion.",trigger_hint_due_date:"The task becomes due {days} days before the date the sensor reports.",trigger_hint_due_date_one:"The task becomes due one day before the date the sensor reports.",trigger_hint_due_date_same_day:"The task becomes due on the date the sensor reports.",trigger_hint_due_date_now:"Reported date: {date}.",trigger_hint_state_change:"The task becomes due after {count} state change(s).",trigger_hint_state_change_to:"The task becomes due after {count} change(s) to \u201C{state}\u201D.",trigger_hint_state_now:"Current state: {value}.",adopt_problem_part:"Uses part: {name}",label_filter:"Label",all_labels:"All labels",settings_notify_scope:"Notify only for view",settings_notify_scope_all:"All tasks",settings_notify_scope_hint:"Only tasks matching the selected saved view's label/user filters send reminders. Status, sorting and grouping of the view are ignored here.",settings_notify_completed:"Completion notifications",settings_notify_completed_hint:`Household news when a task is completed, with the reason and who did it. "Automatic only" announces just the completions nobody made on the spot: sensor recovery, shopping-list check-off, an automation's service call. Sent to the household service only; quiet hours and the daily cap apply.`,settings_title_style:"Notification title style",settings_title_style_hint:"What appears as the notification's TITLE. Default keeps the per-status text (e.g. 'Maintenance overdue!'). Object name / Task name help when phone notifications stack.",title_style_default:"Default (per-status title)",title_style_object_name:"Object name as title",title_style_task_name:"Task name as title",notify_completed_off:"Off",notify_completed_automatic:"Automatic completions only",notify_completed_all:"All completions",settings_notify_rule:"Your own notification rule",settings_notify_rule_hint:"Every notification also fires the event maintenance_supporter_notification (kind, status, object, task, reference numbers, priority, deep link, the payload). Route it with an automation \u2014 e.g. into Ticker, Telegram or Pushover.",settings_notify_event_only:"Only fire the event, send nothing myself",settings_notify_event_only_hint:"Your automation is the whole delivery. Rate limits, quiet hours and the daily cap still apply to when the event fires.",settings_notify_extra_data:"Extra notification data (template)",settings_notify_extra_data_hint:"JSON or YAML, Jinja allowed; merged into the notify call's data (your keys win). Variables: kind, status, priority, labels, notes, area_name, object_name, task_name, object_ref, task_ref, days_until_due, next_due, last_performed, responsible_user_id, sensor_entity_id, url, target, title, message. Test it with the Send test button.",card_saved_view:"Saved view",card_saved_view_none:"None",card_saved_view_help:"Applies the view's status, user, label and priority filters on top of the filters above. The view's sorting and grouping are panel display settings and are not applied on the card.",panel_card_tab:"Open on tab",panel_card_tab_default:"Remembered tab (as in the panel)",panel_card_view:"Open with saved view",panel_card_height:"Height",panel_card_height_help:"Empty = fill the screen below the card (panel view). Otherwise a CSS length such as 600px or 80vh for views with other cards.",panel_card_not_registered:"The Maintenance Supporter panel is not available \u2014 is the integration set up?",panel_card_load_failed:"The Maintenance Supporter panel could not be loaded \u2014 reload the page (after an update: clear the browser cache).",doc_part_none:"No documents linked to this part.",settings_templates_toggle_group:"Enable or disable all templates in this group",setups_button:"Suggested setups",setups_title:"Suggested setups (Beta)",setups_hint:"Devices of supported integrations whose consumable sensors can drive maintenance tasks. Adopting creates the object and wires each task to its sensor \u2014 it triggers when the consumable runs low and resolves itself after replacement.",setups_none:"No supported devices with unwired consumable sensors found.",setups_adopt:"Set up selected",setups_done:"{tasks} sensor-wired tasks created.",setups_done_one:"1 sensor-wired task created.",complete_parts_used:"Parts used this time",part_delete_confirm:"Delete part '{name}'? Its stock tracking, task links and any open buy reminder will be removed.",baseline_start_value:"Start reading (optional)",baseline_start_help:"Counting starts from this reading. Leave empty to count from the current value; enter the reading at the last service so usage since then already counts.",setups_baseline_hint:"reading at last service (optional)",baseline_start_help_edit:"Leave empty to keep the existing counting. Entering a value re-anchors the counting (e.g. the reading at the last service).",baseline_current_effective:"Currently effective start value: {value}",runtime_on_states:"Active states",runtime_on_states_help:"States that count as running \u2014 default: on. E.g. mowing, cleaning, printing. With an attribute selected, its values are matched instead.",runtime_max_session:"Max session runtime (seconds)",runtime_max_session_help:"A single run counts at most this many seconds \u2014 protects against a sensor stuck ON (lost connection, restart). Empty = no cap.",trend_approaching:"Heading toward the threshold",trend_easing:"Easing away from the threshold",split_select_hint:"Select a task to see its details here",setups_target_new:"Create new: {name}",setups_target_match:"{name} \u2014 matches: {reasons}",setups_reason_model:"model number",setups_reason_name:"name",setups_reason_area:"area",setups_reason_sibling:"same model on another device",setups_maybe_covered:'Probably there already as "{name}"',setups_already:"Already there: {names}",setups_reset_line:"Completing it also resets the counter in the integration (\u201C{button}\u201D)",setups_reset_enable:"\u2014 the button gets switched on for this",reset_offers_title:"Reset counters on completion",reset_offers_hint:"These tasks watch a counter the integration keeps itself. Connected, completing the task here also presses the integration's reset button \u2014 otherwise the counter keeps running and the task falls due again right away.",reset_offers_before:"Now: completing it leaves the counter in {integration} running",reset_offers_after:"Then: it also presses \u201C{button}\u201D",reset_offers_renamed:"Renamed since it was set up \u2014 check that this task is for this part",reset_offers_apply:"Connect resets",reset_offers_done:"{count} tasks now reset their counter on completion",reset_offers_done_one:"1 task now resets its counter on completion",setups_before_after:"Tasks on the object: {before} \u2192 {after}",setups_new_object_count:'New object "{name}" \xB7 tasks: {count}',schedule_preview_title:"Next dates",schedule_preview_ontime:"Assuming on-time completion.",schedule_preview_ends:"(series ends)",adopt_problem_responsible:"Responsible user for all adopted tasks (optional)",adopt_for_minutes_hint:"Only trigger once the problem has persisted this long \u2014 0 reacts to the first flicker.",adopt_problem_configure:"Configure",history_auto:"Automatic",battery_fleet_title:"Battery fleet",battery_fleet_none_low:"All batteries OK \u2014 nothing to replace.",battery_fleet_progress:"{n} to replace",battery_fleet_buy_now:"Buy now",battery_fleet_soon:"Needed soon",battery_fleet_soon_hint:"Predicted from the last replacement date \u2014 order ahead.",battery_fleet_mark_all:"Mark all replaced",battery_fleet_mark_one:"Mark this battery replaced",battery_fleet_offline:"offline",battery_fleet_no_sensor:"No sensor",battery_fleet_trigger_lost:"This task's sensor trigger was lost \u2014 it will not fire or auto-complete.",battery_fleet_repair:"Repair",battery_fleet_exclude:"Exclude from the fleet",battery_fleet_excluded:"Excluded",battery_fleet_include:"Track again",battery_fleet_all:"All tracked batteries",battery_fleet_all_hint:"Exclude a device here to drop it from the fleet before it ever reports low \u2014 a vacuum that recharges itself, or a phone that warns you on its own.",battery_fleet_add:"Add a battery",battery_fleet_add_hint:"Pick a battery sensor the automatic discovery missed \u2014 it joins the roster immediately.",battery_fleet_track_self:"Track self-charging batteries",battery_fleet_track_self_hint:"Phones, vacuums and other devices that recharge themselves appear as rechargeables \u2014 a low one asks for a charge, never for new cells.",battery_fleet_due_without_sensor:"Treat a passed forecast as due when a battery has no sensor",battery_fleet_due_without_sensor_hint:"Some devices report no battery level at all \u2014 Battery Notes only knows the type and the last replacement. Once the typical lifetime has passed, such a battery counts as low and the fleet task reminds you; marking it replaced resets the forecast.",battery_fleet_status_low:"Low",battery_fleet_status_soon:"Soon",battery_fleet_status_ok:"Healthy",battery_fleet_status_due:"Due",battery_fleet_predicted_on:"Expected around {date}",battery_fleet_predicted_typical:"Expected around {date} \u2014 {months} months typical for {type} ({source})",lifetime_source_default:"built-in default",lifetime_source_table:"built-in table",lifetime_source_learned:"learned from {n} replacements",lifetime_source_override:"your setting",lifetime_source_learned_device:"learned from this device's {n} replacements",lifetime_source_learned_model:"learned from {n} replacements on devices of this model",battery_fleet_predicted_trend:"Predicted from this battery's discharge trend: around {date} ({confidence})",battery_fleet_rechargeable:"Rechargeable: charge instead of replacing \u2014 never on the shopping list",battery_fleet_sort_name:"Sort by name",battery_fleet_sort_urgency:"Sort by urgency",battery_fleet_mark_recharged:"Mark as recharged",battery_fleet_sparkline_hint:"Battery level over the last 30 days \u2014 dotted: projected until the low threshold",battery_fleet_filter_type:"Show only this battery type",battery_fleet_record_replacement:"The level jumped around {date} \u2014 record this replacement in Battery Notes",battery_fleet_total:"{n} batteries tracked",battery_fleet_setup_button:"Battery fleet",battery_fleet_setup_done:"Battery fleet set up \u2014 one task tracks all your batteries.",update_banner:"A newer version of Maintenance Supporter is on the server \u2014 reload to update the panel.",update_reload:"Reload",row_actions_banner:"Complete and Skip in task rows are now buttons. Prefer the old icons?",row_actions_keep:"Keep buttons",row_actions_back:"Back to icons",battery_fleet_forecast_overdue:"Predicted date passed \u2014 the battery still reports healthy. If you swapped it, record the replacement; otherwise the forecast was off.",cost_from_parts:"Use \u2248 {amount} from parts",cost_parts_booked:"Parts used \u2248 {amount} are booked automatically \u2014 enter only additional costs.",cost_parts_info:"Parts used \u2248 {amount} \u2014 counted when they were bought, not here.",cost_purchase_use_hint:"The price paid becomes the part's price \u2014 spare parts count when they are used.",history_parts_counted:"Parts: {amount}",history_parts_counted_hint:"Value of the parts used \u2014 counted as spending",history_parts_info:"Parts \u2248 {amount}",history_parts_info_hint:"Value of the parts used \u2014 information only; the money counted when they were bought",history_purchase_stock:"Bought for {amount} (counted when used)",dismiss:"Dismiss",gs_label:"Getting started \u2014 these hints retire as your setup grows",gs_setups_chip:"Suggested setups found {n} devices with pre-wired triggers",gs_setups_chip_one:"Suggested setups found 1 device with pre-wired triggers",gs_adopt_chip:"{n} problem sensors can become maintenance tasks",gs_adopt_chip_one:"1 problem sensor can become a maintenance task",gs_fleet_chip:"One click sets up the battery fleet",gs_reset_chip:"{n} tasks can reset their counter in the integration",gs_reset_chip_one:"1 task can reset its counter in the integration",cal_editor_window:"Default window",cal_editor_window_week:"Week (7 days)",cal_editor_window_fortnight:"Fortnight (14 days)",cal_editor_window_month:"Month (30 days, default)",cal_editor_window_year:"Year (365 days, empty days collapsed)",cal_editor_window_past_30:"Past 30 days (history)",cal_editor_window_past_90:"Past 90 days (history)",cal_editor_show_chips:"Show window chips inside the card",cal_editor_chips_hint:"Hide the chips when the card is embedded in a strategy view that already serves as the window selector.",cal_editor_show_user_filter:"Show user filter dropdown",cal_editor_default_user:"Default user filter",cal_editor_my_tasks:"My tasks (current user)",cal_editor_show_object_filter:"Show object filter dropdown",cal_editor_object_hint:'Pre-select one object via YAML: object_filter: "" \u2014 or a list of names to restrict the card to several objects.',object_history_section:"History (all tasks)",object_history_all_tasks:"All tasks",object_history_empty:"No entries in this range.",object_history_cap_note:"History keeps up to 500 entries per task \u2014 very old entries may be missing.",service_record_title:"Service record",service_record_print:"Service record (PDF)",date:"Date",service_record_entries:"entries",ref_number:"Reference number",print_options_title:"Print service record",print_layout:"Layout",print_layout_chronological:"Chronological",print_layout_by_task:"By task",print_include:"Include",print_inc_readings:"Readings",print_inc_parts:"Parts used",print_inc_photos:"Photos",print_inc_documents:"Linked documents",print_inc_doc_desc:"Document descriptions",print_inc_checklist:"Checklist",print_inc_notes:"Notes",print_inc_costs:"Cost and duration",print_inc_person:"Completed by",print_inc_refs:"Reference numbers",print_inc_bare:"Completions without details",print_inc_qr:"QR code per task",print_button:"Print",report_scan_hint:"Scan to open the task",completed_by:"Completed by",date_from:"From",date_to:"To",phases_section:"Cycle phases (optional)",phases_hint:"Different work on one shared schedule \u2014 each completion moves to the next step (e.g. small service, small service, big service).",phase_add:"Add phase",phase_name:"Phase name",phase_sequence_label:"Cycle order",phase_sequence_add_step:"Add step",phase_current:"Current phase",phase_set:"Set as current",chart_history_fallback:"No long-term statistics for this entity \u2014 showing recorder state history (typically the last ~10 days)",chart_history_alarm:"Trigger view from recorder state history: 1 = alert state held for the hold time, 0 = fine (typically the last ~10 days)",chart_history_count:"Change count rebuilt from recorder state history since the last service (typically the last ~10 days)",prediction_cycles:"Learned from cycles",phase_require_override:"Override \u201CRequire on completion\u201D for this phase",history_add_past:"Add past completion",require_tag_scan:"Only complete by scanning the tag",require_tag_scan_help:"Proof of presence: Done is blocked on every surface until the NFC tag or QR code on the thing itself is scanned. Automations can pass 'via_tag_scan' to the complete service.",disallow_skip:"Don't allow skipping",disallow_skip_help:"The Skip action disappears from every surface, and the server refuses a skip from automations and voice too.",no_notifications:"No notifications for this task",no_notifications_help:"No reminders for this task \u2014 no status notifications, repeats, lead-time reminders or bundles. It still shows on the dashboard and in its entities; the weekly digest still counts it.",require_tag_scan_hint:"This task completes only by scanning its NFC tag or QR code on the thing itself \u2014 saving here will be refused."}});var ps,us=y(()=>{"use strict";ps="2.98.0"});function Oi(s){Pe.en=Object.assign({},s,Pe.en??{})}function ye(s){let r=(s||Hi).toLowerCase();return r.startsWith("pt")&&r.endsWith("br")?"pt-br":r.substring(0,2)}function n(s,r,e){let t=ye(r);return Pe[t]?.[s]??Pe.en?.[s]??e??s}function Le(s){let r=ye(s);return r===Hi||r in Pe}function Ne(s){let r=ye(s);return r===Hi||r in Pe||!Ba.has(r)?Promise.resolve():(r in bt||(bt[r]=fetch(`${Wa}/${r}.json?v=${ps}`).then(e=>e.ok?e.json():null).then(e=>{e?Pe[r]=e:delete bt[r]}).catch(()=>{delete bt[r]})),bt[r])}var Hi,hs,Pe,bt,Ba,Wa,Wt=y(()=>{"use strict";us();Hi="en",hs=(()=>{let s=window;return s.__msLocales||(s.__msLocales={store:{},inflight:{}}),s.__msLocales})(),Pe=hs.store,bt=hs.inflight;Ba=new Set(["de","nl","fr","it","es","pt","pt-br","ru","uk","pl","cs","sv","zh","da","fi","nb","ja","hi","hu","ko","tr"]),Wa="/maintenance_supporter_locales"});function _s(s){let r=s?.language;!r||typeof s?.loadBackendTranslation!="function"||ie.lang===r&&(ie.localize||ie.inflight)||(ie.lang=r,ie.localize=null,ie.inflight=s.loadBackendTranslation("exceptions",ji).then(e=>{ie.lang===r&&(ie.localize=e)}).catch(()=>{}).finally(()=>{ie.inflight=null}))}function ms(s,r,e){if(!r||!ie.localize||s&&s!==ji)return null;let t=`component.${ji}.exceptions.${r}.message`,i=ie.localize(t,e??void 0);return i&&i!==t?i:null}var ji,ie,Di=y(()=>{"use strict";ji="maintenance_supporter",ie=(()=>{let s=window;return s.__msBackendErrors??={lang:"",localize:null,inflight:null}})()});function He(s){return s?.currency_symbol||Va}function gs(s,r){r.has("hass")&&zi(s.hass?.locale,s.hass?.config?.country,s.hass?.config?.time_zone);let e=s.hass?.language;e&&!Le(e)&&Ne(e).then(()=>s.requestUpdate()),_s(s.hass)}function N(s){return s?.language||"en"}function fs(s){let r=ye(s);return{de:"de-DE",en:"en-US",nl:"nl-NL",fr:"fr-FR",it:"it-IT",es:"es-ES",pt:"pt-PT",ru:"ru-RU",uk:"uk-UA",zh:"zh-CN",da:"da-DK",fi:"fi-FI",nb:"nb-NO",ja:"ja-JP",hi:"hi-IN",pl:"pl-PL",cs:"cs-CZ",sv:"sv-SE","pt-br":"pt-BR",hu:"hu-HU",ko:"ko-KR",tr:"tr-TR"}[r]??"en-US"}function Ga(s){ys.decimals=typeof s=="number"&&Number.isInteger(s)&&s>=0&&s<=3?s:0}function Ka(){return ys.decimals??0}function Vt(s){s&&s.currency_decimals!==void 0&&s.currency_decimals!==null&&Ga(Number(s.currency_decimals))}function zi(s,r,e){e!==void 0&&(re.serverTimeZone=e||void 0),s&&(re.date=s.date_format,re.time=s.time_format,re.number=s.number_format,re.timeZone=s.time_zone,r!==void 0&&(re.country=r||void 0))}function Ya(s){switch(re.number){case"comma_decimal":return["en-US","en"];case"decimal_comma":return["de","es","it"];case"space_comma":return["fr","sv","cs"];case"system":return;default:return fs(s)}}function C(s,r,e){if(!Number.isFinite(s))return String(s);let t=typeof e=="number"?{minimumFractionDigits:e,maximumFractionDigits:e}:{maximumFractionDigits:2,...e};if(re.number==="none"){let i=Math.pow(10,t.maximumFractionDigits??2);return String(Math.round(s*i)/i)}try{return new Intl.NumberFormat(Ya(r),t).format(s)}catch{return new Intl.NumberFormat(void 0,t).format(s)}}function J(s,r,e,t){let i=t??Ka(),a=Math.abs(s)<.5/Math.pow(10,i)?0:s,o=C(a,e,i);return r?`${o} ${r}`:o}function Ye(s,r,e){let t=C(s,e);return r?`${t} ${r}`:t}function Ge(s){let r=fs(s),e=re.country;if(e&&/^[A-Za-z]{2}$/.test(e)){let t=`${ye(s).split("-")[0]}-${e.toUpperCase()}`;try{return new Intl.DateTimeFormat(t),t}catch{}}return r}function Mi(s,r,e){let t=re.date;if(t==="DMY"||t==="MDY"||t==="YMD"){let[a,o,d]=ve(s,e).split("-");return t==="DMY"?`${d}/${o}/${a}`:t==="MDY"?`${o}/${d}/${a}`:`${a}-${o}-${d}`}let i=t==="system"?void 0:Ge(r);return s.toLocaleDateString(i,{day:"2-digit",month:"2-digit",year:"numeric",timeZone:e})}function Gt(s,r){let e=fe();switch(re.time){case"12":return s.toLocaleTimeString(Ge(r),{hour:"2-digit",minute:"2-digit",hour12:!0,timeZone:e});case"24":return s.toLocaleTimeString(Ge(r),{hour:"2-digit",minute:"2-digit",hour12:!1,timeZone:e});case"system":return s.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit",timeZone:e});default:return s.toLocaleTimeString(Ge(r),{hour:"2-digit",minute:"2-digit",timeZone:e})}}function O(s,r){if(!s)return"\u2014";try{let e=bs.exec(s);return e?Mi(new Date(Date.UTC(+e[1],+e[2]-1,+e[3])),r,"UTC"):Mi(new Date(Q(s)),r,fe())}catch{return s}}function Kt(s,r){if(!s)return"\u2014";if(bs.test(s))return O(s,r);try{let e=new Date(Q(s));return Mi(e,r,fe())+" "+Gt(e,r)}catch{return s}}function Yt(s,r=Date.now()){let e=ge();return Ut(r,e)===Ut(r,fe())?"":n("ha_time_zone_hint",s).replace("{zone}",e)}function xs(s,r){if(s==null)return"\u2014";let e=r||"en";return s<0?`${Math.abs(s)} ${n("d_overdue",e)}`:s===0?n("today",e):`${s} ${s===1?n("day",e):n("days",e)}`}function qe(s,r,e){return s==null?"\u2014":`${s} ${n("unit_"+(r||"days"),e)}`}function Qt(s,r){return s==null?"\u2014":`${C(s,r,{maximumFractionDigits:1})} ${n("minutes_short",r)}`}function Ke(s,r,e="long"){return new Date(Date.UTC(2024,0,1+s)).toLocaleDateString(Ge(r),{weekday:e,timeZone:"UTC"})}function Fi(s,r,e="long"){return new Date(Date.UTC(2024,s,1)).toLocaleDateString(Ge(r),{month:e,timeZone:"UTC"})}function ws(s,r,e="long"){return Ke((s.getDay()+6)%7,r,e)}function ks(s,r,e="long"){return Fi(s.getMonth(),r,e)}function $s(s,r){let e=s.schedule;if(s.schedule_type==="sensor_based"){let i=n("sensor_based",r),a=e&&e.kind==="interval"&&e.every?e.every:s.interval_days;return a?`${i} \xB7 ${qe(a,(e&&e.kind==="interval"?e.unit:s.interval_unit)||"days",r)}`:i}let t=e?.offset?` ${e.offset>0?"+":"\u2212"}${Math.abs(e.offset)}d`:"";switch(e?.kind){case"weekdays":return((e.weekdays||[]).map(i=>Ke(i,r,"short")).join(" & ")||"\u2014")+t;case"nth_weekday":return e.weekday==null||e.nth==null?"\u2014":`${e.nth===-1?n("ord_last",r):n("ord_"+e.nth,r)} ${Ke(e.weekday,r,"long")}${t}`;case"day_of_month":return e.day==null?"\u2014":(e.day===-1?n(e.business?"last_business_day_month":"last_day_month",r):`${n("day_word",r)} ${e.day}`)+t;case"calendar":return`${n("calendar_entity_label",r)}: ${s.schedule_entity_name||e.entity_id||"\u2014"}${t}`;case"one_time":return s.due_date?O(s.due_date,r):n("one_time",r);case"manual":return n("manual",r);case"interval":return qe(e.every,e.unit,r)}return s.schedule_type==="one_time"?s.due_date?O(s.due_date,r):n("one_time",r):s.schedule_type==="manual"?n("manual",r):s.schedule_type==="sensor_based"?n("sensor_based",r):s.interval_days!=null?qe(s.interval_days,s.interval_unit,r):"\u2014"}function Es(s,r){s.currentTarget.dispatchEvent(new CustomEvent("hass-more-info",{detail:{entityId:r},bubbles:!0,composed:!0}))}var Va,vs,re,ys,bs,Qe,Qa,Za,Zt,T=y(()=>{"use strict";I();cs();zt();Wt();Di();Z();Wt();Va="\u20AC";Oi(ds);vs=window,re=vs.__msDateTimePrefs??={},ys=vs.__msMoneyPrefs??={};bs=/^(\d{4})-(\d{2})-(\d{2})$/;Qe=E` .field { display: flex; flex-direction: column; gap: 4px; } .field-label { font-size: 12px; color: var(--secondary-text-color); } .field-input { @@ -14,7 +14,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" font-family: inherit; width: 100%; box-sizing: border-box; } .field-input:focus { outline: none; border-color: var(--primary-color); } -`,ka=E` +`,Qa=E` .person-chip { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; vertical-align: middle; } .person-avatar { width: 22px; height: 22px; border-radius: 50%; flex: none; @@ -24,7 +24,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" font-variant-numeric: tabular-nums; } .person-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; } -`,$a=E` +`,Za=E` .ref-chip { display: inline-block; font-size: 11.5px; font-weight: 500; line-height: 1; padding: 3px 6px; border-radius: 6px; border: 1px solid var(--divider-color); color: var(--secondary-text-color); @@ -33,9 +33,9 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" } /* #189: "· Residual waste, Paper" after a calendar-driven task's name. */ .event-titles { color: var(--secondary-text-color); font-weight: 400; } -`,Ut=E` - ${ka} - ${$a} +`,Zt=E` + ${Qa} + ${Za} :host { --maint-ok-color: var(--success-color, #4caf50); --maint-due-soon-color: var(--warning-color, #ff9800); @@ -1270,7 +1270,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" .stat-item .stat-label { font-size: 11px; white-space: normal; text-align: center; line-height: 1.2; hyphens: auto; overflow-wrap: anywhere; } .stat-value { font-size: 20px; } } -`});function Bt(s){if(!s)return[];let r=[],e=s.photo_doc_id;typeof e=="string"&&r.push(e);let t=s.photo_doc_ids;Array.isArray(t)&&r.push(...t);let i=[];for(let a of r){if(typeof a!="string")continue;let o=a.trim();if(!(!o||i.includes(o))&&(i.push(o),i.length>=10))break}return i}var Wt=y(()=>{"use strict"});function as(){let s=new Uint8Array(4);return(globalThis.crypto??{getRandomValues:r=>r.map(()=>Math.floor(Math.random()*256))}).getRandomValues(s),Array.from(s,r=>r.toString(16).padStart(2,"0")).join("")}function me(s){let r=s?.reading_values;if(!Array.isArray(r))return[];let e=[],t=new Set;for(let i of r){if(!i||typeof i!="object")continue;let a=i;typeof a.id!="string"||!a.id||t.has(a.id)||typeof a.value!="number"||!Number.isFinite(a.value)||(t.add(a.id),e.push({id:a.id,name:typeof a.name=="string"?a.name:a.id,unit:typeof a.unit=="string"?a.unit:null,value:a.value}))}return e}function os(s){return s.filter(r=>r.type==="completed"&&(r.reading_value!=null||me(r).length>0)).sort((r,e)=>r.timestamp.localeCompare(e.timestamp))}function ls(s){return os(s??[]).map(r=>({timestamp:r.timestamp,values:me(r)})).filter(r=>r.values.length>0)}function cs(s,r,e){let t;for(let i of s){if(e!==void 0){let o=new Date(i.timestamp).getTime();if(!isNaN(o)&&o>=e)break}let a=i.values.find(o=>o.id===r);a&&(t=a)}return t}function ds(s,r,e){let t=me(r).find(a=>a.id===e);if(!t)return null;let i=null;for(let a of os(s)){if(a.timestamp>=r.timestamp)break;let o=me(a).find(d=>d.id===e);o&&(i=o.value)}return i==null?null:t.value-i}function ps(s){let r=new Set,e=new Set;for(let t of s){let i=(t.name||"").trim().toLowerCase();i&&(r.has(i)?e.add(t.id):r.add(i))}return e}function us(s){let r=[],e=new Set,t=new Set;for(let i of s){let a=(i.name||"").trim();if(!(!a||e.has(i.id)||t.has(a.toLowerCase()))&&(e.add(i.id),t.add(a.toLowerCase()),r.push({id:i.id,name:a,unit:(i.unit||"").trim()||null}),r.length>=20))break}return r}var Ie=y(()=>{"use strict"});function Ai(s,r,e,t){let i=e,a=u=>typeof u=="string"?u:null,o=u=>typeof u=="number"?u:null,d=a(i.timestamp)??"";return{entry_id:s,task_id:r,original_timestamp:d,type:a(i.type)||"completed",timestamp:d,notes:a(i.notes),cost:o(i.cost),duration:o(i.duration),completed_by:a(i.completed_by),used_parts:Array.isArray(i.used_parts)?i.used_parts:null,photo_doc_ids:Bt(i),reading_value:o(i.reading_value),reading_values:me(i),readings:t?.readings??[],task_type:t?.type??null,reading_unit:t?.reading_unit??null}}async function hs(s,r,e,t){let a=(await s.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:r})).tasks?.find(d=>d.id===e);if(!a)return null;let o=a.history?.find(d=>d.timestamp===t);return o||(o=(await s.connection.sendMessagePromise({type:"maintenance_supporter/task/history",entry_id:r,task_id:e})).history?.find(u=>u.timestamp===t)),o?Ai(r,e,o,a):null}var Ci=y(()=>{"use strict";Wt();Ie()});function ge(s,r=Re){return s?s.is_admin?!0:r.operatorWriteEnabled&&r.operatorIds.includes(s.id):!1}var Re,_t=y(()=>{"use strict";Re={operatorWriteEnabled:!1,operatorIds:[]}});var _s,ms,gs,mt,gt,ft,Ii,fe,We,vt=y(()=>{"use strict";_s={default_warning_days:[0,365],default_consumable_threshold:[1,90],battery_low_percent:[1,90],battery_recovered_percent:[20,100],archive_oneoff_days:[0,3650],delete_archived_oneoff_days:[0,3650],notify_due_soon_interval_hours:[0,720],notify_overdue_interval_hours:[0,720],notify_triggered_interval_hours:[0,720],max_notifications_per_day:[0,1e3],notification_bundle_threshold:[2,20],snooze_duration_hours:[1,168],warranty_reminder_days:[1,365],budget_alert_threshold:[10,100],currency_decimals:[0,3]},ms=[0,1440],gs=[1,1e4],mt=[0,365],gt=15,ft=[.1,.9],Ii=[0,365],fe=[.01,1e4],We=[.01,1e5]});function Sa(s){let[r,e]=_s.default_warning_days,t=s?.general?.default_warning_days,i=s?.general?.row_action_style;return{features:{...Vt.features,...s?.features??{}},defaultWarningDays:typeof t=="number"&&Number.isFinite(t)&&t>=r&&t<=e?t:Vt.defaultWarningDays,rowActionStyle:Ea.includes(i??"")?i:Vt.rowActionStyle,access:{operatorWriteEnabled:s?.operator_write_enabled===!0,operatorIds:Array.isArray(s?.admin_panel_user_ids)?s.admin_panel_user_ids.filter(a=>typeof a=="string"):[]},budget:s?.budget??null,rowActionNoticePending:s?.general?.row_action_notice_pending===!0,refsInLists:s?.general?.ref_numbers_in_lists===!0,objectsTableColumns:Array.isArray(s?.objects_table_columns)?s.objects_table_columns:void 0,partsCostMode:Cr(s?.budget?.parts_cost_mode)}}function fs(){let s=window;return s.__msSettingsCache??={promise:null}}function ie(s){let r=fs();if(r.promise)return r.promise;let e=s.connection.sendMessagePromise({type:"maintenance_supporter/settings"}).then(t=>Sa(t)).catch(()=>(r.promise===e&&(r.promise=null),Vt));return r.promise=e,e}function vs(){fs().promise=null}var Ea,Vt,yt=y(()=>{"use strict";_t();vt();Lt();Ea=["buttons_compact","buttons","icons"];Vt={features:{adaptive:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1},defaultWarningDays:7,rowActionStyle:"buttons_compact",access:Re,budget:null,rowActionNoticePending:!1,refsInLists:!1,objectsTableColumns:void 0,partsCostMode:"purchase"}});function Ca(s){let r=s.replace(/_ids?$/,"").split("_").filter(Boolean);if(r.length===0)return s;let e=r.join(" ");return e.charAt(0).toUpperCase()+e.slice(1)}function Ia(s,r){let e=Aa[s];if(e){let t=n(e,r);if(t&&t!==e)return t}return Ca(s)}function Ra(s){let r=[...s.matchAll(/\['([^']+)'\]/g)].map(i=>i[1]),e=r.length?r[r.length-1]:void 0,t;return(t=s.match(/length of value must be at most (\d+)/))?{field:e,rule:"too_long",param:t[1]}:(t=s.match(/length of value must be at least (\d+)/))?{field:e,rule:"too_short",param:t[1]}:(t=s.match(/value must be at most (\S+)/))?{field:e,rule:"value_too_high",param:t[1]}:(t=s.match(/value must be at least (\S+)/))?{field:e,rule:"value_too_low",param:t[1]}:/required key not provided/.test(s)?{field:e,rule:"required"}:(t=s.match(/expected (\w+)/))?{field:e,rule:"wrong_type",param:t[1]}:/value must be one of/.test(s)?{field:e,rule:"invalid_choice"}:/not a valid value/.test(s)?{field:e,rule:"invalid_value"}:{field:e,rule:"unknown"}}function Pa(s,r){let e=new Set(ys(r));return ys(s).every(t=>e.has(t))}function bs(s,r,e){if(e=e??n("action_error",r),typeof s=="string")return s;if(typeof s!="object"||s===null)return e;let t=s,i=t.message||t.error?.message||"",a=t.code||t.error?.code||"",o=a?Ta[a]:void 0,d=Gr(t.translation_domain??t.error?.translation_domain,t.translation_key??t.error?.translation_key,t.translation_placeholders??t.error?.translation_placeholders);if(d)return d;if(!i&&!o)return e;let u=Ra(i),_=u.field?Ia(u.field,r):"",m=f=>n(f,r).replace("{field}",_).replace("{n}",u.param??"");switch(u.rule){case"too_long":return m("err_too_long");case"too_short":return m("err_too_short");case"value_too_high":return m("err_value_too_high");case"value_too_low":return m("err_value_too_low");case"required":return m("err_required");case"wrong_type":return m("err_wrong_type").replace("{type}",u.param??"");case"invalid_choice":return m("err_invalid_choice");case"invalid_value":return m("err_invalid_value");default:if(o){let f=n(o,r);return _e(r)==="en"&&i&&!Pa(i,a)?`${f} (${i})`:f}return i||e}}var Ta,Aa,ys,xs=y(()=>{"use strict";T();Ei();Ot();Ta={already_archived:"ws_err_already_archived",already_paused:"ws_err_already_paused",archived:"ws_err_archived",create_failed:"ws_err_create_failed",duplicate_failed:"ws_err_duplicate_failed",empty:"ws_err_empty",empty_csv:"ws_err_empty_csv",invalid_cursor:"ws_err_invalid_cursor",invalid_date:"ws_err_invalid_date",invalid_device:"ws_err_invalid_device",invalid_entity_slug:"ws_err_invalid_entity_slug",invalid_format:"ws_err_invalid_format",invalid_input:"ws_err_invalid_input",invalid_mirror_todo:"ws_err_invalid_mirror_todo",invalid_parent:"ws_err_invalid_parent",invalid_icon:"ws_err_invalid_icon",invalid_range:"ws_err_invalid_range",invalid_search_template:"ws_err_invalid_search_template",invalid_target:"ws_err_invalid_target",invalid_trigger_config:"ws_err_invalid_trigger_config",invalid_url:"ws_err_invalid_url",invalid_user:"ws_err_invalid_user",invalid_view:"ws_err_invalid_view",limit_reached:"ws_err_limit_reached",no_defaults:"ws_err_no_defaults",no_phases:"ws_err_no_phases",no_url:"ws_err_no_url",not_archived:"ws_err_not_archived",not_available:"ws_err_not_available",not_configured:"ws_err_not_configured",not_found:"ws_err_not_found",not_loaded:"ws_err_not_loaded",not_paused:"ws_err_not_paused",replace_failed:"ws_err_replace_failed",self_link_device:"ws_err_self_link_device",storage_unavailable:"ws_err_storage_unavailable",too_early:"ws_err_too_early",too_large:"ws_err_too_large",too_many:"ws_err_too_many",too_many_views:"ws_err_too_many_views",unauthorized:"ws_err_unauthorized",unavailable:"ws_err_unavailable"},Aa={entry_id:"object",name:"name",task_type:"maintenance_type",schedule_type:"schedule_type",interval_days:"interval_days",interval_anchor:"interval_anchor",warning_days:"warning_days",last_performed:"last_performed_optional",notes:"notes_optional",documentation_url:"documentation_url_optional",custom_icon:"custom_icon_optional",nfc_tag_id:"nfc_tag_id_optional",responsible_user_id:"responsible_user",entity_slug:"entity_slug",entity_id:"entity_id",area_id:"area_id_optional",manufacturer:"manufacturer_optional",model:"model_optional",serial_number:"serial_number_optional",installation_date:"installation_date_optional",warranty_expiry:"warranty_expiry_optional",checklist:"checklist_steps_optional",reason:"reason",feedback:"feedback",cost:"cost",duration:"duration",description:"description_optional",environmental_entity:"environmental_entity_optional",environmental_attribute:"environmental_attribute_optional",trigger_above:"trigger_above",trigger_below:"trigger_below",trigger_equals:"trigger_equals",trigger_not_equals:"trigger_not_equals",trigger_for_minutes:"trigger_for_minutes"};ys=s=>s.toLowerCase().split(/[^a-z0-9]+/).filter(r=>r.length>1)});async function j(s,r,e={}){e.busy?.(!0);try{let t=typeof r=="function"?await r():await s.hass.connection.sendMessagePromise(r);return e.reload&&await e.reload(),e.successToast&&e.onSuccess?.(e.successToast),t===void 0?null:t}catch(t){let i=e.lang||L(s.hass);e.onError?.(bs(t,i,e.fallbackKey?n(e.fallbackKey,i):void 0),t);return}finally{e.busy?.(!1)}}var Pe=y(()=>{"use strict";xs();T()});var O,Ri=y(()=>{"use strict";C();z();O=class extends S{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1;this.multiline=!1;this.rows=3}_onInput(e){let t=e.target.value;this.value=t,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:t}}))}render(){return l` +`});function Jt(s){if(!s)return[];let r=[],e=s.photo_doc_id;typeof e=="string"&&r.push(e);let t=s.photo_doc_ids;Array.isArray(t)&&r.push(...t);let i=[];for(let a of r){if(typeof a!="string")continue;let o=a.trim();if(!(!o||i.includes(o))&&(i.push(o),i.length>=10))break}return i}var Xt=y(()=>{"use strict"});function As(){let s=new Uint8Array(4);return(globalThis.crypto??{getRandomValues:r=>r.map(()=>Math.floor(Math.random()*256))}).getRandomValues(s),Array.from(s,r=>r.toString(16).padStart(2,"0")).join("")}function be(s){let r=s?.reading_values;if(!Array.isArray(r))return[];let e=[],t=new Set;for(let i of r){if(!i||typeof i!="object")continue;let a=i;typeof a.id!="string"||!a.id||t.has(a.id)||typeof a.value!="number"||!Number.isFinite(a.value)||(t.add(a.id),e.push({id:a.id,name:typeof a.name=="string"?a.name:a.id,unit:typeof a.unit=="string"?a.unit:null,value:a.value}))}return e}function Cs(s){return s.filter(r=>r.type==="completed"&&(r.reading_value!=null||be(r).length>0)).sort((r,e)=>r.timestamp.localeCompare(e.timestamp))}function Is(s){return Cs(s??[]).map(r=>({timestamp:r.timestamp,values:be(r)})).filter(r=>r.values.length>0)}function Rs(s,r,e){let t;for(let i of s){if(e!==void 0){let o=Q(i.timestamp);if(!isNaN(o)&&o>=e)break}let a=i.values.find(o=>o.id===r);a&&(t=a)}return t}function Ps(s,r,e){let t=be(r).find(a=>a.id===e);if(!t)return null;let i=null;for(let a of Cs(s)){if(a.timestamp>=r.timestamp)break;let o=be(a).find(d=>d.id===e);o&&(i=o.value)}return i==null?null:t.value-i}function Ls(s){let r=new Set,e=new Set;for(let t of s){let i=(t.name||"").trim().toLowerCase();i&&(r.has(i)?e.add(t.id):r.add(i))}return e}function Ns(s){let r=[],e=new Set,t=new Set;for(let i of s){let a=(i.name||"").trim();if(!(!a||e.has(i.id)||t.has(a.toLowerCase()))&&(e.add(i.id),t.add(a.toLowerCase()),r.push({id:i.id,name:a,unit:(i.unit||"").trim()||null}),r.length>=Ui))break}return r}var Ui,Oe=y(()=>{"use strict";Z();Ui=20});function Bi(s,r,e,t){let i=e,a=p=>typeof p=="string"?p:null,o=p=>typeof p=="number"?p:null,d=a(i.timestamp)??"";return{entry_id:s,task_id:r,original_timestamp:d,type:a(i.type)||"completed",timestamp:d,notes:a(i.notes),cost:o(i.cost),duration:o(i.duration),completed_by:a(i.completed_by),used_parts:Array.isArray(i.used_parts)?i.used_parts:null,photo_doc_ids:Jt(i),reading_value:o(i.reading_value),reading_values:be(i),readings:t?.readings??[],task_type:t?.type??null,reading_unit:t?.reading_unit??null}}async function qs(s,r,e,t){let a=(await s.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:r})).tasks?.find(d=>d.id===e);if(!a)return null;let o=a.history?.find(d=>d.timestamp===t);return o||(o=(await s.connection.sendMessagePromise({type:"maintenance_supporter/task/history",entry_id:r,task_id:e})).history?.find(p=>p.timestamp===t)),o?Bi(r,e,o,a):null}var Wi=y(()=>{"use strict";Xt();Oe()});function xe(s,r=je){return s?s.is_admin?!0:r.operatorWriteEnabled&&r.operatorIds.includes(s.id):!1}var je,xt=y(()=>{"use strict";je={operatorWriteEnabled:!1,operatorIds:[]}});var Hs,Os,js,wt,kt,$t,Vi,we,Ze,Et=y(()=>{"use strict";Hs={default_warning_days:[0,365],default_consumable_threshold:[1,90],battery_low_percent:[1,90],battery_recovered_percent:[20,100],archive_oneoff_days:[0,3650],delete_archived_oneoff_days:[0,3650],notify_due_soon_interval_hours:[0,720],notify_overdue_interval_hours:[0,720],notify_triggered_interval_hours:[0,720],max_notifications_per_day:[0,1e3],notification_bundle_threshold:[2,20],snooze_duration_hours:[1,168],warranty_reminder_days:[1,365],budget_alert_threshold:[10,100],currency_decimals:[0,3]},Os=[0,1440],js=[1,1e4],wt=[0,365],kt=15,$t=[.1,.9],Vi=[0,365],we=[.01,1e4],Ze=[.01,1e5]});function Xa(s){let[r,e]=Hs.default_warning_days,t=s?.general?.default_warning_days,i=s?.general?.row_action_style;return{features:{...ei.features,...s?.features??{}},defaultWarningDays:typeof t=="number"&&Number.isFinite(t)&&t>=r&&t<=e?t:ei.defaultWarningDays,rowActionStyle:Ja.includes(i??"")?i:ei.rowActionStyle,access:{operatorWriteEnabled:s?.operator_write_enabled===!0,operatorIds:Array.isArray(s?.admin_panel_user_ids)?s.admin_panel_user_ids.filter(a=>typeof a=="string"):[]},budget:s?.budget??null,rowActionNoticePending:s?.general?.row_action_notice_pending===!0,refsInLists:s?.general?.ref_numbers_in_lists===!0,objectsTableColumns:Array.isArray(s?.objects_table_columns)?s.objects_table_columns:void 0,partsCostMode:Wr(s?.budget?.parts_cost_mode)}}function Ds(){let s=window;return s.__msSettingsCache??={promise:null}}function ne(s){let r=Ds();if(r.promise)return r.promise;let e=s.connection.sendMessagePromise({type:"maintenance_supporter/settings"}).then(t=>Xa(t)).catch(()=>(r.promise===e&&(r.promise=null),ei));return r.promise=e,e}function Ms(){Ds().promise=null}var Ja,ei,St=y(()=>{"use strict";xt();Et();Mt();Ja=["buttons_compact","buttons","icons"];ei={features:{adaptive:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1},defaultWarningDays:7,rowActionStyle:"buttons_compact",access:je,budget:null,rowActionNoticePending:!1,refsInLists:!1,objectsTableColumns:void 0,partsCostMode:"purchase"}});function io(s){let r=s.replace(/_ids?$/,"").split("_").filter(Boolean);if(r.length===0)return s;let e=r.join(" ");return e.charAt(0).toUpperCase()+e.slice(1)}function ro(s,r){let e=to[s];if(e){let t=n(e,r);if(t&&t!==e)return t}return io(s)}function so(s){let r=[...s.matchAll(/\['([^']+)'\]/g)].map(i=>i[1]),e=r.length?r[r.length-1]:void 0,t;return(t=s.match(/length of value must be at most (\d+)/))?{field:e,rule:"too_long",param:t[1]}:(t=s.match(/length of value must be at least (\d+)/))?{field:e,rule:"too_short",param:t[1]}:(t=s.match(/value must be at most (\S+)/))?{field:e,rule:"value_too_high",param:t[1]}:(t=s.match(/value must be at least (\S+)/))?{field:e,rule:"value_too_low",param:t[1]}:/required key not provided/.test(s)?{field:e,rule:"required"}:(t=s.match(/expected (\w+)/))?{field:e,rule:"wrong_type",param:t[1]}:/value must be one of/.test(s)?{field:e,rule:"invalid_choice"}:/not a valid value/.test(s)?{field:e,rule:"invalid_value"}:{field:e,rule:"unknown"}}function no(s,r){let e=new Set(zs(r));return zs(s).every(t=>e.has(t))}function Fs(s,r,e){if(e=e??n("action_error",r),typeof s=="string")return s;if(typeof s!="object"||s===null)return e;let t=s,i=t.message||t.error?.message||"",a=t.code||t.error?.code||"",o=a?eo[a]:void 0,d=ms(t.translation_domain??t.error?.translation_domain,t.translation_key??t.error?.translation_key,t.translation_placeholders??t.error?.translation_placeholders);if(d)return d;if(!i&&!o)return e;let p=so(i),_=p.field?ro(p.field,r):"",m=f=>n(f,r).replace("{field}",_).replace("{n}",p.param??"");switch(p.rule){case"too_long":return m("err_too_long");case"too_short":return m("err_too_short");case"value_too_high":return m("err_value_too_high");case"value_too_low":return m("err_value_too_low");case"required":return m("err_required");case"wrong_type":return m("err_wrong_type").replace("{type}",p.param??"");case"invalid_choice":return m("err_invalid_choice");case"invalid_value":return m("err_invalid_value");default:if(o){let f=n(o,r);return ye(r)==="en"&&i&&!no(i,a)?`${f} (${i})`:f}return i||e}}var eo,to,zs,Us=y(()=>{"use strict";T();Di();Wt();eo={already_archived:"ws_err_already_archived",already_paused:"ws_err_already_paused",archived:"ws_err_archived",changed_since:"undo_changed",create_failed:"ws_err_create_failed",duplicate_failed:"ws_err_duplicate_failed",empty:"ws_err_empty",empty_csv:"ws_err_empty_csv",invalid_cursor:"ws_err_invalid_cursor",invalid_date:"ws_err_invalid_date",invalid_device:"ws_err_invalid_device",invalid_entity_slug:"ws_err_invalid_entity_slug",invalid_format:"ws_err_invalid_format",invalid_input:"ws_err_invalid_input",invalid_mirror_todo:"ws_err_invalid_mirror_todo",invalid_parent:"ws_err_invalid_parent",invalid_icon:"ws_err_invalid_icon",invalid_range:"ws_err_invalid_range",invalid_search_template:"ws_err_invalid_search_template",invalid_target:"ws_err_invalid_target",invalid_trigger_config:"ws_err_invalid_trigger_config",invalid_url:"ws_err_invalid_url",invalid_user:"ws_err_invalid_user",invalid_view:"ws_err_invalid_view",limit_reached:"ws_err_limit_reached",no_defaults:"ws_err_no_defaults",no_phases:"ws_err_no_phases",no_url:"ws_err_no_url",not_archived:"ws_err_not_archived",not_available:"ws_err_not_available",not_configured:"ws_err_not_configured",not_found:"ws_err_not_found",not_loaded:"ws_err_not_loaded",not_paused:"ws_err_not_paused",nothing_to_undo:"undo_nothing",replace_failed:"ws_err_replace_failed",self_link_device:"ws_err_self_link_device",storage_unavailable:"ws_err_storage_unavailable",too_early:"ws_err_too_early",too_large:"ws_err_too_large",too_many:"ws_err_too_many",too_many_views:"ws_err_too_many_views",unauthorized:"ws_err_unauthorized",unavailable:"ws_err_unavailable"},to={entry_id:"object",name:"name",task_type:"maintenance_type",schedule_type:"schedule_type",interval_days:"interval_days",interval_anchor:"interval_anchor",warning_days:"warning_days",last_performed:"last_performed_optional",notes:"notes_optional",documentation_url:"documentation_url_optional",custom_icon:"custom_icon_optional",nfc_tag_id:"nfc_tag_id_optional",responsible_user_id:"responsible_user",entity_slug:"entity_slug",entity_id:"entity_id",area_id:"area_id_optional",manufacturer:"manufacturer_optional",model:"model_optional",serial_number:"serial_number_optional",installation_date:"installation_date_optional",warranty_expiry:"warranty_expiry_optional",checklist:"checklist_steps_optional",reason:"reason",feedback:"feedback",cost:"cost",duration:"duration",description:"description_optional",environmental_entity:"environmental_entity_optional",environmental_attribute:"environmental_attribute_optional",trigger_above:"trigger_above",trigger_below:"trigger_below",trigger_equals:"trigger_equals",trigger_not_equals:"trigger_not_equals",trigger_for_minutes:"trigger_for_minutes"};zs=s=>s.toLowerCase().split(/[^a-z0-9]+/).filter(r=>r.length>1)});async function j(s,r,e={}){e.busy?.(!0);try{let t=typeof r=="function"?await r():await s.hass.connection.sendMessagePromise(r);return e.reload&&await e.reload(),e.successToast&&e.onSuccess?.(e.successToast),t===void 0?null:t}catch(t){let i=e.lang||N(s.hass);e.onError?.(Fs(t,i,e.fallbackKey?n(e.fallbackKey,i):void 0),t);return}finally{e.busy?.(!1)}}var De=y(()=>{"use strict";Us();T()});var D,Gi=y(()=>{"use strict";I();F();D=class extends S{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1;this.multiline=!1;this.rows=3}_onInput(e){let t=e.target.value;this.value=t,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:t}}))}render(){return l` - `}};O.styles=E` + `}};D.styles=E` :host { display: block; } .field { display: flex; @@ -1347,20 +1347,20 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" color: var(--secondary-text-color); font-style: italic; } - `,c([v()],O.prototype,"label",2),c([v()],O.prototype,"value",2),c([v()],O.prototype,"placeholder",2),c([v()],O.prototype,"type",2),c([v({type:Boolean})],O.prototype,"required",2),c([v({type:Boolean})],O.prototype,"disabled",2),c([v()],O.prototype,"step",2),c([v()],O.prototype,"min",2),c([v()],O.prototype,"max",2),c([v()],O.prototype,"pattern",2),c([v()],O.prototype,"helper",2),c([v({type:Boolean})],O.prototype,"multiline",2),c([v({type:Number})],O.prototype,"rows",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",O)});function La(s){let e=F("2001-02-03",s).match(/\d+/g)||[],t=d=>e.findIndex(u=>Number(u)===d),i=t(2001),a=t(2),o=t(3);return i===0?"YMD":a>=0&&o>=0&&a9999||r<1||r>12||e<1||e>31)return null;let t=new Date(s,r-1,e);return t.getFullYear()!==s||t.getMonth()!==r-1||t.getDate()!==e?null:`${s}-${String(r).padStart(2,"0")}-${String(e).padStart(2,"0")}`}function ws(s,r){let e=s.trim(),t;if(t=e.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/))return Ve(+t[1],+t[2],+t[3]);if(t=e.match(/^(\d{4})$/))return Ve(+t[1],1,1);if(t=e.match(/^(\d{1,2})[./](\d{4})$/))return Ve(+t[2],+t[1],1);if(t=e.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/))return Ve(+t[3],+t[2],+t[1]);if(t=e.match(/^(\d{1,2})[/-](\d{1,2})[/-](\d{4})$/)){let i=+t[1],a=+t[2],o=+t[3];return La(r)==="MDY"?Ve(o,i,a):Ve(o,a,i)}return null}var ks=y(()=>{"use strict";T()});function Na(s,r){if(r)switch(s){case"date":return r.split("T")[0];case"time":return r.length===5?`${r}:00`:r;case"datetime":{let[e,t="00:00:00"]=r.split("T");return`${e} ${t.length===5?`${t}:00`:t}`}}}function qa(s,r){if(typeof r!="string"||!r)return"";switch(s){case"date":return r.slice(0,10);case"time":return r.slice(0,5);case"datetime":{let[e,t="00:00:00"]=r.split(" ");return`${e}T${t.length===5?`${t}:00`:t}`}}}var U,Ge=y(()=>{"use strict";C();z();T();ks();U=class extends S{constructor(){super(...arguments);this.kind="date";this.label="";this.value="";this.clearable=!1;this.disabled=!1;this.required=!1;this.lang="en";this._typing=!1;this._typed="";this._typedInvalid=!1}_startTyping(){this._typed=this.value,this._typedInvalid=!1,this._typing=!0,this.updateComplete.then(()=>this.shadowRoot?.querySelector("input.typed")?.focus())}_commitTyped(){if(!this._typed.trim()){this._typing=!1;return}let e=ws(this._typed,this.lang);if(!e){this._typedInvalid=!0;return}this._typing=!1,this._typedInvalid=!1,this._emit(e)}_onTypedKey(e){e.key==="Enter"?(e.preventDefault(),this._commitTyped()):e.key==="Escape"&&(e.preventDefault(),this._typing=!1,this._typedInvalid=!1)}_selector(){switch(this.kind){case"date":return{date:{}};case"time":return{time:{no_second:!0}};case"datetime":return{datetime:{}}}}_onSelectorChange(e){e.stopPropagation(),this._emit(qa(this.kind,e.detail?.value))}_clear(){this._emit("")}_emit(e){e!==this.value&&(this.value=e,this.dispatchEvent(new CustomEvent("value-changed",{bubbles:!0,composed:!0,detail:{value:e}})))}render(){return l` + `,c([v()],D.prototype,"label",2),c([v()],D.prototype,"value",2),c([v()],D.prototype,"placeholder",2),c([v()],D.prototype,"type",2),c([v({type:Boolean})],D.prototype,"required",2),c([v({type:Boolean})],D.prototype,"disabled",2),c([v()],D.prototype,"step",2),c([v()],D.prototype,"min",2),c([v()],D.prototype,"max",2),c([v()],D.prototype,"pattern",2),c([v()],D.prototype,"helper",2),c([v({type:Boolean})],D.prototype,"multiline",2),c([v({type:Number})],D.prototype,"rows",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",D)});function ao(s){let e=O("2001-02-03",s).match(/\d+/g)||[],t=d=>e.findIndex(p=>Number(p)===d),i=t(2001),a=t(2),o=t(3);return i===0?"YMD":a>=0&&o>=0&&a9999||r<1||r>12||e<1||e>31)return null;let t=new Date(s,r-1,e);return t.getFullYear()!==s||t.getMonth()!==r-1||t.getDate()!==e?null:`${s}-${String(r).padStart(2,"0")}-${String(e).padStart(2,"0")}`}function Bs(s,r){let e=s.trim(),t;if(t=e.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/))return Je(+t[1],+t[2],+t[3]);if(t=e.match(/^(\d{4})$/))return Je(+t[1],1,1);if(t=e.match(/^(\d{1,2})[./](\d{4})$/))return Je(+t[2],+t[1],1);if(t=e.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/))return Je(+t[3],+t[2],+t[1]);if(t=e.match(/^(\d{1,2})[/-](\d{1,2})[/-](\d{4})$/)){let i=+t[1],a=+t[2],o=+t[3];return ao(r)==="MDY"?Je(o,i,a):Je(o,a,i)}return null}var Ws=y(()=>{"use strict";T()});function oo(s,r){if(r)switch(s){case"date":return r.split("T")[0];case"time":return r.length===5?`${r}:00`:r;case"datetime":{let[e,t="00:00:00"]=r.split("T");return`${e} ${t.length===5?`${t}:00`:t}`}}}function lo(s,r){if(typeof r!="string"||!r)return"";switch(s){case"date":return r.slice(0,10);case"time":return r.slice(0,5);case"datetime":{let[e,t="00:00:00"]=r.split(" ");return`${e}T${t.length===5?`${t}:00`:t}`}}}var U,Xe=y(()=>{"use strict";I();F();T();Ws();U=class extends S{constructor(){super(...arguments);this.kind="date";this.label="";this.value="";this.clearable=!1;this.disabled=!1;this.required=!1;this.lang="en";this._typing=!1;this._typed="";this._typedInvalid=!1}_startTyping(){this._typed=this.value,this._typedInvalid=!1,this._typing=!0,this.updateComplete.then(()=>this.shadowRoot?.querySelector("input.typed")?.focus())}_commitTyped(){if(!this._typed.trim()){this._typing=!1;return}let e=Bs(this._typed,this.lang);if(!e){this._typedInvalid=!0;return}this._typing=!1,this._typedInvalid=!1,this._emit(e)}_onTypedKey(e){e.key==="Enter"?(e.preventDefault(),this._commitTyped()):e.key==="Escape"&&(e.preventDefault(),this._typing=!1,this._typedInvalid=!1)}_selector(){switch(this.kind){case"date":return{date:{}};case"time":return{time:{no_second:!0}};case"datetime":return{datetime:{}}}}_onSelectorChange(e){e.stopPropagation(),this._emit(lo(this.kind,e.detail?.value))}_clear(){this._emit("")}_emit(e){e!==this.value&&(this.value=e,this.dispatchEvent(new CustomEvent("value-changed",{bubbles:!0,composed:!0,detail:{value:e}})))}render(){return l`
${this.label?l`${this.label}${this.required?l`*`:h}`:h}
${this._typing?l`{this._typed=e.target.value,this._typedInvalid=!1}} @keydown=${this._onTypedKey} @blur=${this._commitTyped} />`:l`{"use strict";C();z();T();Pe();Ri();Ge();P=class extends S{constructor(){super(...arguments);this.objects=[];this._open=!1;this._loading=!1;this._error="";this._name="";this._manufacturer="";this._model="";this._serialNumber="";this._areaId="";this._installationDate="";this._warrantyExpiry="";this._documentationUrl="";this._notes="";this._haDeviceId="";this._parentEntryId="";this._entryId=null;this._replacing=!1;this._oldDeviceId="";this._replaceDevice="keep"}get _lang(){return L(this.hass)}openCreate(){this._entryId=null,this._name="",this._manufacturer="",this._model="",this._serialNumber="",this._areaId="",this._installationDate="",this._warrantyExpiry="",this._documentationUrl="",this._notes="",this._haDeviceId="",this._parentEntryId="",this._replacing=!1,this._error="",this._open=!0}openReplace(e,t){this._entryId=e,this._replacing=!0,this._name=t.name||"",this._oldDeviceId=t.ha_device_id||"",this._haDeviceId="",this._replaceDevice="keep",this._error="",this._open=!0}openEdit(e,t){this._entryId=e,this._name=t.name||"",this._manufacturer=t.manufacturer||"",this._model=t.model||"",this._serialNumber=t.serial_number||"",this._areaId=t.area_id||"",this._installationDate=t.installation_date||"",this._warrantyExpiry=t.warranty_expiry||"",this._documentationUrl=t.documentation_url||"",this._notes=t.notes||"",this._haDeviceId=t.ha_device_id||"",this._parentEntryId=t.parent_entry_id||"",this._replacing=!1,this._error="",this._open=!0}async _replace(){if(this._loading||!this._entryId)return;let e={type:"maintenance_supporter/object/replace",entry_id:this._entryId,name:this._name.trim()||null};this._oldDeviceId?(this._replaceDevice==="other"&&(e.ha_device_id=this._haDeviceId),this._replaceDevice==="none"&&(e.ha_device_id=null)):this._haDeviceId&&(e.ha_device_id=this._haDeviceId);let t=await j(this,e,{busy:i=>{this._loading=i},fallbackKey:"save_error",onError:i=>{this._error=i}});t!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-replaced",{detail:{entry_id:t?.entry_id,device_swap:t?.device_swap}})))}_deviceName(e){let i=this.hass?.devices?.[e];return i?.name_by_user||i?.name||e}_deviceSelector(e){return l`{"use strict";I();F();T();De();Gi();Xe();P=class extends S{constructor(){super(...arguments);this.objects=[];this._open=!1;this._loading=!1;this._error="";this._name="";this._manufacturer="";this._model="";this._serialNumber="";this._areaId="";this._installationDate="";this._warrantyExpiry="";this._documentationUrl="";this._notes="";this._haDeviceId="";this._parentEntryId="";this._entryId=null;this._replacing=!1;this._oldDeviceId="";this._replaceDevice="keep"}get _lang(){return N(this.hass)}openCreate(){this._entryId=null,this._name="",this._manufacturer="",this._model="",this._serialNumber="",this._areaId="",this._installationDate="",this._warrantyExpiry="",this._documentationUrl="",this._notes="",this._haDeviceId="",this._parentEntryId="",this._replacing=!1,this._error="",this._open=!0}openReplace(e,t){this._entryId=e,this._replacing=!0,this._name=t.name||"",this._oldDeviceId=t.ha_device_id||"",this._haDeviceId="",this._replaceDevice="keep",this._error="",this._open=!0}openEdit(e,t){this._entryId=e,this._name=t.name||"",this._manufacturer=t.manufacturer||"",this._model=t.model||"",this._serialNumber=t.serial_number||"",this._areaId=t.area_id||"",this._installationDate=t.installation_date||"",this._warrantyExpiry=t.warranty_expiry||"",this._documentationUrl=t.documentation_url||"",this._notes=t.notes||"",this._haDeviceId=t.ha_device_id||"",this._parentEntryId=t.parent_entry_id||"",this._replacing=!1,this._error="",this._open=!0}async _replace(){if(this._loading||!this._entryId)return;let e={type:"maintenance_supporter/object/replace",entry_id:this._entryId,name:this._name.trim()||null};this._oldDeviceId?(this._replaceDevice==="other"&&(e.ha_device_id=this._haDeviceId),this._replaceDevice==="none"&&(e.ha_device_id=null)):this._haDeviceId&&(e.ha_device_id=this._haDeviceId);let t=await j(this,e,{busy:i=>{this._loading=i},fallbackKey:"save_error",onError:i=>{this._error=i}});t!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-replaced",{detail:{entry_id:t?.entry_id,device_swap:t?.device_swap}})))}_deviceName(e){let i=this.hass?.devices?.[e];return i?.name_by_user||i?.name||e}_deviceSelector(e){return l`>>0;return Es[r%Es.length]}function Ss(s){return s?{id:s.id,name:s.name,initials:s.initials||Ha(s.name),color:s.color||ja(s.id)}:null}var Es,Ts=y(()=>{"use strict";C();Es=["#c62828","#ad1457","#6a1b9a","#4527a0","#283593","#1565c0","#00838f","#2e7d32","#558b2f","#ef6c00","#6d4c41","#546e7a"]});var Gt,As=y(()=>{"use strict";Ts();Gt=class{constructor(r){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=r}updateHass(r){this.hass=r}async getUsers(r=!1){let e=Date.now();if(!r&&this.usersCache&&e-this.cacheTimestampt.id===r)?.name||null}getPerson(r){return Ss(this.getUser(r))}getUser(r){return!r||!this.usersCache?null:this.usersCache.find(e=>e.id===r)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(r){return r?r===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}}});function re(s){return`${s.entry_id??""}\0${s.part_id}`}function Cs(s,r,e,t){let i=!!s.entry_id&&s.entry_id!==r,a=i?s.entry_id:r,o=e.find(m=>m.entry_id===a),d=(o?.parts||[]).find(m=>m.id===s.part_id)||null,u=i&&o?.object?.name||"",_=d?.name||n("shared_part_unknown",t);return{part:d,foreign:i,ownerName:u,label:u?`${_} (${u})`:_}}function Is(s,r,e,t){let{part:i,label:a}=Cs(s,r,e,t),o=i&&i.stock!==null&&i.stock!==void 0?` (${Ue(i.stock,i.unit,t)})`:"",d=i?.storage_location?` \u2014 ${i.storage_location}`:"";return`${I(s.quantity,t)}\xD7 ${a}${o}${d}`}function Rs(s,r,e,t){let a=(e.find(d=>d.entry_id===r)?.parts||[]).map(d=>({...d})),o=new Set(a.map(d=>re({part_id:d.id})));for(let d of s?.consumes_parts||[]){if(!d.entry_id||d.entry_id===r)continue;let u=re(d);if(o.has(u))continue;o.add(u);let{part:_,ownerName:m}=Cs(d,r,e,t);a.push({id:d.part_id,name:_?.name||n("shared_part_unknown",t),unit:_?.unit,stock:_?.stock??null,storage_location:_?.storage_location,entry_id:d.entry_id,owner_name:m})}return a}var Kt=y(()=>{"use strict";T()});var Pi,Ps,Ls,Ns=y(()=>{"use strict";Pi=["sensor","binary_sensor","number","input_number","input_boolean","switch","climate","vacuum","cover","fan","light","water_heater","humidifier","media_player","weather","air_quality","valve","lawn_mower","lock"],Ps=["sensor"],Ls=["temperature","humidity","pressure"]});function Ke(s){let r=(s??"").trim();if(r==="")return null;let e=Number(r.replace(",","."));return!Number.isFinite(e)||e<0?null:Math.round(e)}var Yt=y(()=>{"use strict"});var Li,bt,Ni=y(()=>{"use strict";Li=["notes","cost","duration","photo","user"],bt={notes:"notes_label",cost:"cost",duration:"duration",photo:"photo_label",user:"user_label"}});var Oa,Da,qi,qs=y(()=>{"use strict";Oa={cleaning:"mdi:broom",inspection:"mdi:magnify",replacement:"mdi:swap-horizontal",calibration:"mdi:tune",service:"mdi:wrench",reading:"mdi:counter",custom:"mdi:wrench-clock"},Da="mdi:wrench-clock",qi=s=>s&&Oa[s]||Da});var ve,Qt=y(()=>{"use strict";C();z();T();ve=class extends S{constructor(){super(...arguments);this.value="";this.lang="en";this._credit=!1;this._amount=""}willUpdate(e){if(!e.has("value"))return;let t=(this.value??"").trim();if(t===this._signed())return;let i=parseFloat(t);if(t===""||Number.isNaN(i)){this._amount="",this._credit=!1;return}this._credit=i<0,this._amount=String(Math.abs(i))}_signed(){let e=this._amount.trim();return e===""?"":this._credit?`-${e}`:e}_emit(){this.value=this._signed(),this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:this.value},bubbles:!0,composed:!0}))}_setCredit(e){this._credit!==e&&(this._credit=e,this._emit())}_onInput(e){let t=e.target.value;t.startsWith("-")&&(this._credit=!0),this._amount=t.replace(/^-+/,""),this._emit()}render(){let e=this.lang;return l` + `,c([v({attribute:!1})],P.prototype,"hass",2),c([v({attribute:!1})],P.prototype,"objects",2),c([u()],P.prototype,"_open",2),c([u()],P.prototype,"_loading",2),c([u()],P.prototype,"_error",2),c([u()],P.prototype,"_name",2),c([u()],P.prototype,"_manufacturer",2),c([u()],P.prototype,"_model",2),c([u()],P.prototype,"_serialNumber",2),c([u()],P.prototype,"_areaId",2),c([u()],P.prototype,"_installationDate",2),c([u()],P.prototype,"_warrantyExpiry",2),c([u()],P.prototype,"_documentationUrl",2),c([u()],P.prototype,"_notes",2),c([u()],P.prototype,"_haDeviceId",2),c([u()],P.prototype,"_parentEntryId",2),c([u()],P.prototype,"_entryId",2),c([u()],P.prototype,"_replacing",2),c([u()],P.prototype,"_oldDeviceId",2),c([u()],P.prototype,"_replaceDevice",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",P)});function co(s){let r=(s||"").split(/\s+/).filter(Boolean);return r.length===0?"?":r.length===1?r[0][0].toUpperCase():(r[0][0]+r[r.length-1][0]).toUpperCase()}function po(s){let r=0;for(let e of s)r=r*31+e.charCodeAt(0)>>>0;return Gs[r%Gs.length]}function Ks(s){return s?{id:s.id,name:s.name,initials:s.initials||co(s.name),color:s.color||po(s.id)}:null}var Gs,Ys=y(()=>{"use strict";I();Gs=["#c62828","#ad1457","#6a1b9a","#4527a0","#283593","#1565c0","#00838f","#2e7d32","#558b2f","#ef6c00","#6d4c41","#546e7a"]});var ti,Qs=y(()=>{"use strict";Ys();ti=class{constructor(r){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=r}updateHass(r){this.hass=r}async getUsers(r=!1){let e=Date.now();if(!r&&this.usersCache&&e-this.cacheTimestampt.id===r)?.name||null}getPerson(r){return Ks(this.getUser(r))}getUser(r){return!r||!this.usersCache?null:this.usersCache.find(e=>e.id===r)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(r){return r?r===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}}});function ae(s){return`${s.entry_id??""}\0${s.part_id}`}function Zs(s,r,e,t){let i=!!s.entry_id&&s.entry_id!==r,a=i?s.entry_id:r,o=e.find(m=>m.entry_id===a),d=(o?.parts||[]).find(m=>m.id===s.part_id)||null,p=i&&o?.object?.name||"",_=d?.name||n("shared_part_unknown",t);return{part:d,foreign:i,ownerName:p,label:p?`${_} (${p})`:_}}function Js(s,r,e,t){let{part:i,label:a}=Zs(s,r,e,t),o=i&&i.stock!==null&&i.stock!==void 0?` (${Ye(i.stock,i.unit,t)})`:"",d=i?.storage_location?` \u2014 ${i.storage_location}`:"";return`${C(s.quantity,t)}\xD7 ${a}${o}${d}`}function Xs(s,r,e,t){let a=(e.find(d=>d.entry_id===r)?.parts||[]).map(d=>({...d})),o=new Set(a.map(d=>ae({part_id:d.id})));for(let d of s?.consumes_parts||[]){if(!d.entry_id||d.entry_id===r)continue;let p=ae(d);if(o.has(p))continue;o.add(p);let{part:_,ownerName:m}=Zs(d,r,e,t);a.push({id:d.part_id,name:_?.name||n("shared_part_unknown",t),unit:_?.unit,stock:_?.stock??null,storage_location:_?.storage_location,entry_id:d.entry_id,owner_name:m})}return a}var ii=y(()=>{"use strict";T()});var Ki,en,tn,rn=y(()=>{"use strict";Ki=["sensor","binary_sensor","number","input_number","input_boolean","switch","climate","vacuum","cover","fan","light","water_heater","humidifier","media_player","weather","air_quality","valve","lawn_mower","lock"],en=["sensor"],tn=["temperature","humidity","pressure"]});function et(s){let r=(s??"").trim();if(r==="")return null;let e=Number(r.replace(",","."));return!Number.isFinite(e)||e<0?null:Math.round(e)}var ri=y(()=>{"use strict"});var Yi,Tt,Qi=y(()=>{"use strict";Yi=["notes","cost","duration","photo","user"],Tt={notes:"notes_label",cost:"cost",duration:"duration",photo:"photo_label",user:"user_label"}});var uo,ho,Zi,sn=y(()=>{"use strict";uo={cleaning:"mdi:broom",inspection:"mdi:magnify",replacement:"mdi:swap-horizontal",calibration:"mdi:tune",service:"mdi:wrench",reading:"mdi:counter",custom:"mdi:wrench-clock"},ho="mdi:wrench-clock",Zi=s=>s&&uo[s]||ho});var ke,si=y(()=>{"use strict";I();F();T();ke=class extends S{constructor(){super(...arguments);this.value="";this.lang="en";this._credit=!1;this._amount=""}willUpdate(e){if(!e.has("value"))return;let t=(this.value??"").trim();if(t===this._signed())return;let i=parseFloat(t);if(t===""||Number.isNaN(i)){this._amount="",this._credit=!1;return}this._credit=i<0,this._amount=String(Math.abs(i))}_signed(){let e=this._amount.trim();return e===""?"":this._credit?`-${e}`:e}_emit(){this.value=this._signed(),this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:this.value},bubbles:!0,composed:!0}))}_setCredit(e){this._credit!==e&&(this._credit=e,this._emit())}_onInput(e){let t=e.target.value;t.startsWith("-")&&(this._credit=!0),this._amount=t.replace(/^-+/,""),this._emit()}render(){let e=this.lang;return l`
${this._credit?l`
${n("cost_credit_hint",e)}
`:h} - `}};ve.styles=[Be,E` + `}};ke.styles=[Qe,E` :host { display: block; } .wrap { display: flex; gap: 8px; align-items: stretch; } .sign { @@ -1683,9 +1683,9 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" .kind.credit.on { background: var(--success-color, #43a047); } .amount { flex: 1; min-width: 0; } .hint { margin-top: 4px; font-size: 12px; color: var(--secondary-text-color); } - `],c([v()],ve.prototype,"value",2),c([v()],ve.prototype,"lang",2),c([p()],ve.prototype,"_credit",2),c([p()],ve.prototype,"_amount",2);customElements.get("ms-cost-input")||customElements.define("ms-cost-input",ve)});function Os(){return{entityIds:"",type:"threshold",attribute:"",above:"",below:"",equals:"",notEquals:"",forMinutes:"0",targetValue:"",deltaMode:!1,fromState:"",toState:"",targetChanges:"",runtimeHours:"",onStates:"",daysBefore:"0",carry:{}}}function Ms(s){return{type:s.type||"threshold",attribute:s.attribute||"",above:s.trigger_above?.toString()??"",below:s.trigger_below?.toString()??"",equals:s.trigger_equals?.toString()??"",notEquals:s.trigger_not_equals?.toString()??"",forMinutes:s.trigger_for_minutes?.toString()??"0",targetValue:s.trigger_target_value?.toString()??"",deltaMode:s.trigger_delta_mode||!1,fromState:s.trigger_from_state||"",toState:s.trigger_to_state||"",targetChanges:s.trigger_target_changes?.toString()??"",runtimeHours:s.trigger_runtime_hours?.toString()??"",onStates:(s.trigger_on_states||[]).join(", "),daysBefore:s.trigger_days_before?.toString()??"0"}}function zs(s,r){r.attribute&&(s.attribute=r.attribute);let e=parseInt(r.forMinutes,10);if(r.type==="threshold"){let t=parseFloat(r.above);isNaN(t)||(s.trigger_above=t);let i=parseFloat(r.below);isNaN(i)||(s.trigger_below=i);let a=parseFloat(r.equals);isNaN(a)||(s.trigger_equals=a);let o=parseFloat(r.notEquals);isNaN(o)||(s.trigger_not_equals=o),isNaN(e)||(s.trigger_for_minutes=e)}else if(r.type==="counter"){let t=parseFloat(r.targetValue);isNaN(t)||(s.trigger_target_value=t),s.trigger_delta_mode=r.deltaMode}else if(r.type==="state_change"){r.fromState&&(s.trigger_from_state=r.fromState),r.toState&&(s.trigger_to_state=r.toState);let t=parseInt(r.targetChanges,10);isNaN(t)||(s.trigger_target_changes=t),isNaN(e)||(s.trigger_for_minutes=e)}else if(r.type==="runtime"){let t=parseFloat(r.runtimeHours);isNaN(t)||(s.trigger_runtime_hours=t);let i=(r.onStates||"").split(",").map(a=>a.trim()).filter(Boolean);i.length>0&&(s.trigger_on_states=i)}else if(r.type==="due_date"){let t=parseInt(r.daysBefore,10);s.trigger_days_before=isNaN(t)?0:t}}function Va(s){return{entityIds:(s.entity_ids||(s.entity_id?[s.entity_id]:[])).join(", "),...Ms(s),carry:Object.fromEntries(Object.entries(s).filter(([e])=>!Wa.has(e)&&!e.startsWith("_")))}}function Ga(s){let r=s.entityIds.split(",").map(t=>t.trim()).filter(Boolean);if(r.length===0)return null;let e={...s.carry||{},entity_id:r[0],entity_ids:r,type:s.type};return zs(e,s),e}function Ka(s){return Array.from({length:7},(r,e)=>Fe(e,s,"short"))}function Ya(s){return Array.from({length:12},(r,e)=>Ti(e,s,"short"))}var za,Fa,Ua,Jt,Hs,Ds,js,Ba,oe,Le,Wa,g,Hi,Fs=y(()=>{"use strict";C();z();T();As();Kt();Ie();Ns();Pe();vt();Yt();Ni();Ri();qs();Ge();Qt();za=["cleaning","inspection","replacement","calibration","service","reading","custom"],Fa=["low","normal","high"],Ua=["time_based","weekdays","nth_weekday","day_of_month","calendar","sensor_based","one_time","manual"],Jt=["weekdays","nth_weekday","day_of_month","calendar"],Hs=["time_based","one_time",...Jt],Ds=["threshold","counter","state_change","runtime","due_date"],js=(s,r)=>n(s==="due_date"?"trigger_type_due_date":s,r),Ba=[...Ds,"compound"],oe={alpha:"0.3",min:"7",max:"365"},Le=fe;Wa=new Set(["entity_id","entity_ids","type","attribute","trigger_above","trigger_below","trigger_equals","trigger_not_equals","trigger_for_minutes","trigger_target_value","trigger_delta_mode","trigger_from_state","trigger_to_state","trigger_target_changes","trigger_runtime_hours","trigger_on_states","trigger_days_before"]);g=class g extends S{constructor(){super(...arguments);this.checklistsEnabled=!1;this.scheduleTimeEnabled=!1;this.completionActionsEnabled=!1;this.adaptiveFeature=!1;this.seasonalFeature=!1;this.environmentalFeature=!1;this.defaultWarningDays=7;this.parts=[];this._foreignOwners=[];this._open=!1;this._entityPickerFallback=!1;this._pickerProbeStrikes=0;this._loading=!1;this._error="";this._warning="";this._entryId="";this._taskId=null;this._isFleetTask=!1;this._objectChoices=[];this._name="";this._type="custom";this._scheduleType="time_based";this._intervalDays="30";this._intervalUnit="days";this._dueDate="";this._warningDays="7";this._earliestCompletionDays="";this._intervalAnchor="completion";this._weekdays=[];this._nth="1";this._nthWeekday="5";this._domDay="1";this._domLastDay=!1;this._domBusiness=!1;this._calOffset="0";this._calendarEntity="";this._seasonMonths=[];this._endsMode="never";this._endsCount="";this._endsUntil="";this._schedulePreview=[];this._schedulePreviewEnded=!1;this._previewSeq=0;this._notes="";this._documentationUrl="";this._customIcon="";this._notifyIcon="";this._priority="normal";this._labels="";this._mirrorTodoEntities=[];this._enabled=!0;this._triggerEntityId="";this._triggerEntityIds=[];this._triggerEntityLogic="any";this._triggerAttribute="";this._triggerType="threshold";this._triggerAbove="";this._triggerBelow="";this._triggerEquals="";this._triggerNotEquals="";this._triggerForMinutes="0";this._triggerCombinator="any";this._triggerTargetValue="";this._triggerDeltaMode=!1;this._triggerBaselineValue="";this._liveBaselineValue=null;this._autoCompleteOnRecovery=!1;this._triggerFromState="";this._triggerToState="";this._triggerTargetChanges="";this._triggerRuntimeHours="";this._triggerDaysBefore="0";this._triggerRuntimeMaxSession="";this._triggerOnStates="";this._compoundLogic="AND";this._compoundConditions=[];this._suggestedAttributes=[];this._availableAttributes=[];this._entityDomain="";this._lastPerformed="";this._nfcTagId="";this._requireTagScan=!1;this._allowSkip=!0;this._notifyEnabled=!0;this._readingUnit="";this._readings=[];this._consumesParts={};this._consumesQtyText={};this._partsLoadFailed=!1;this._availableTags=[];this._responsibleUserId=null;this._loadedLastPerformed="";this._loadedResponsibleUserId=null;this._assigneePool=[];this._rotationStrategy="";this._availableUsers=[];this._checklistText="";this._phaseDefs=[];this._phaseSeq=[];this._requiredCompletion=[];this._scheduleTime="";this._scheduleTimeOn=!1;this._actionService="";this._actionTargetEntity="";this._actionTargetStored=null;this._actionTargetInitial="";this._actionSkipAuto=!1;this._actionPresent=!1;this._actionData={};this._actionDataJsonFallback="";this._actionTesting=!1;this._actionTestResult="";this._actionTestError="";this._qcNotes="";this._qcCost="";this._qcDuration="";this._qcFeedback="";this._environmentalEntity="";this._environmentalAttribute="";this._environmentalInitial="";this._environmentalAttributeInitial="";this._adaptiveEnabled=!1;this._adaptiveAlpha=oe.alpha;this._adaptiveMin=oe.min;this._adaptiveMax=oe.max;this._adaptiveSeasonal=!0;this._adaptivePrediction=!0;this._adaptiveInitial="";this._adaptiveWasEnabled=!1;this._userService=null;this._conditionAttrOptions={};this._conditionAttrPending=new Set}_adaptiveSnapshot(){return JSON.stringify([this._adaptiveEnabled,this._adaptiveAlpha,this._adaptiveMin,this._adaptiveMax,this._adaptiveSeasonal,this._adaptivePrediction])}get _lang(){return L(this.hass)}async openCreate(e,t){this._entryId=e,this._taskId=null,this._isFleetTask=!1,this._error="",this._warning="",!e&&t&&t.length>0?(this._objectChoices=t.map(i=>({entry_id:i.entry_id,name:i.object.name})).sort((i,a)=>i.name.localeCompare(a.name)),this._entryId=this._objectChoices[0].entry_id):this._objectChoices=[],this._resetFields(),await Promise.all([this._loadUsers(),this._loadTags(),this._loadParts(),this._loadForeignPools()]),this._open=!0}async openEdit(e,t){this._entryId=e,this._taskId=t.id,this._error="",this._warning="",this._objectChoices=[],this._isFleetTask=t.battery_fleet_task===!0,this._name=t.name,this._type=t.type,this._scheduleType=t.schedule_type,this._intervalDays=t.interval_days!=null?String(t.interval_days):"",this._intervalUnit=t.interval_unit||"days",this._dueDate=t.due_date||"";let i=t.schedule;this._weekdays=i?.kind==="weekdays"?[...i.weekdays??[]]:[],this._nth=i?.kind==="nth_weekday"?String(i.nth??1):"1",this._nthWeekday=i?.kind==="nth_weekday"?String(i.weekday??5):"5",this._domDay=i?.kind==="day_of_month"&&(i.day??1)>=1?String(i.day??1):"1",this._domLastDay=i?.kind==="day_of_month"&&i.day===-1,this._domBusiness=i?.kind==="day_of_month"&&i.business===!0,this._calendarEntity=i?.kind==="calendar"&&i.entity_id||"",this._calOffset=i?.offset?String(i.offset):"0",this._seasonMonths=Array.isArray(i?.season_months)?[...i.season_months]:[];let a=i?.ends;a&&typeof a.count=="number"?(this._endsMode="count",this._endsCount=String(a.count),this._endsUntil=""):a&&typeof a.until=="string"?(this._endsMode="until",this._endsUntil=a.until,this._endsCount=""):(this._endsMode="never",this._endsCount="",this._endsUntil=""),this._warningDays=t.warning_days.toString(),this._earliestCompletionDays=t.earliest_completion_days!=null?String(t.earliest_completion_days):"",this._intervalAnchor=t.interval_anchor||"completion",this._notes=t.notes||"",this._documentationUrl=t.documentation_url||"",this._customIcon=t.custom_icon||"",this._notifyIcon=t.notify_icon||"",this._priority=t.priority||"normal",this._labels=(t.labels||[]).join(", "),this._mirrorTodoEntities=[...t.mirror_todo_entities||[]],this._enabled=t.enabled!==!1,this._lastPerformed=t.last_performed||"",this._loadedLastPerformed=this._lastPerformed,this._nfcTagId=t.nfc_tag_id||"",this._requireTagScan=!!t.require_tag_scan,this._allowSkip=t.allow_skip!==!1,this._notifyEnabled=t.notify_enabled!==!1,this._readingUnit=t.reading_unit||"",this._readings=(t.readings||[]).map(_=>({..._})),this._consumesParts=Object.fromEntries((t.consumes_parts||[]).map(_=>[re(_),{..._}])),this._consumesQtyText={},this._responsibleUserId=t.responsible_user_id||null,this._loadedResponsibleUserId=this._responsibleUserId,this._assigneePool=[...t.assignee_pool||[]],this._rotationStrategy=t.rotation_strategy||"",this._checklistText=(t.checklist||[]).join(` -`),this._phaseDefs=Object.entries(t.phases||{}).map(([_,m])=>{let{name:f,checklist:b,consumes_parts:w,required_completion_fields:k,...q}=m,N=m.consumes_parts||[],B=N.findIndex(W=>!W.entry_id),D=B>=0?N[B]:void 0;return{id:_,name:m.name||_,checklistText:(m.checklist||[]).join(` -`),partId:D?.part_id||"",partQty:D?.quantity!=null?String(D.quantity):"",reqOverride:m.required_completion_fields!==void 0,reqFields:[...m.required_completion_fields||[]],extraParts:N.filter((W,pe)=>pe!==B).map(W=>({...W})),carry:q}}),this._phaseSeq=[...t.phase_sequence||[]],this._requiredCompletion=[...t.required_completion_fields||[]],this._scheduleTime=t.schedule_time||"",this._scheduleTimeOn=!!t.schedule_time;let o=t.on_complete_action;if(o&&o.service){this._actionService=o.service;let _=o.target?.entity_id;this._actionTargetEntity=Array.isArray(_)?_[0]||"":_||"",this._actionTargetStored=o.target&&typeof o.target=="object"?JSON.parse(JSON.stringify(o.target)):null,this._actionTargetInitial=this._actionTargetEntity,this._actionData=o.data&&typeof o.data=="object"?{...o.data}:{},this._actionDataJsonFallback="",this._actionSkipAuto=o.skip_auto===!0,this._actionPresent=!0}else this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1;let d=t.quick_complete_defaults;this._qcNotes=d?.notes||"",this._qcCost=d?.cost!=null?String(d.cost):"",this._qcDuration=d?.duration!=null?String(d.duration):"",this._qcFeedback=d?.feedback||"";let u=t.adaptive_config||{};if(this._environmentalEntity=u.environmental_entity||"",this._environmentalAttribute=u.environmental_attribute||"",this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute,this._adaptiveEnabled=!!u.enabled,this._adaptiveAlpha=u.ewa_alpha?.toString()??oe.alpha,this._adaptiveMin=u.min_interval_days?.toString()??oe.min,this._adaptiveMax=u.max_interval_days?.toString()??oe.max,this._adaptiveSeasonal=u.seasonal_enabled!==!1,this._adaptivePrediction=u.sensor_prediction_enabled!==!1,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=this._adaptiveEnabled,t.trigger_config){let _=t.trigger_config;this._triggerEntityId=_.entity_id||_.entity_ids&&_.entity_ids[0]||"",this._triggerEntityIds=_.entity_ids||(_.entity_id?[_.entity_id]:[]),this._triggerEntityLogic=_.entity_logic||"any",this._setTypeFields(Ms(_)),this._triggerCombinator=_.trigger_combinator==="all"?"all":"any",this._triggerBaselineValue=_.trigger_baseline_value?.toString()||"",this._liveBaselineValue=t.trigger_baseline_value??null,this._autoCompleteOnRecovery=_.auto_complete_on_recovery||!1,this._triggerRuntimeMaxSession=_.trigger_runtime_max_session_seconds?.toString()||"",_.type==="compound"?(this._compoundLogic=_.compound_logic==="OR"?"OR":"AND",this._compoundConditions=(_.conditions||[]).map(Va)):(this._compoundLogic="AND",this._compoundConditions=[])}else this._resetTriggerFields();this._triggerEntityId&&this._fetchEntityAttributes(this._triggerEntityId),await Promise.all([this._loadUsers(),this._loadTags(),this._loadParts(),this._loadForeignPools()]),this._open=!0}_resetFields(){this._name="",this._type="custom",this._scheduleType="time_based",this._intervalDays="30",this._intervalUnit="days",this._dueDate="",this._warningDays=String(this.defaultWarningDays),this._earliestCompletionDays="",this._intervalAnchor="completion",this._weekdays=[],this._nth="1",this._nthWeekday="5",this._domDay="1",this._domLastDay=!1,this._domBusiness=!1,this._calOffset="0",this._seasonMonths=[],this._endsMode="never",this._endsCount="",this._endsUntil="",this._notes="",this._documentationUrl="",this._customIcon="",this._notifyIcon="",this._priority="normal",this._labels="",this._mirrorTodoEntities=[],this._enabled=!0,this._lastPerformed="",this._nfcTagId="",this._requireTagScan=!1,this._allowSkip=!0,this._readingUnit="",this._readings=[],this._consumesParts={},this._consumesQtyText={},this._responsibleUserId=null,this._assigneePool=[],this._rotationStrategy="",this._checklistText="",this._phaseDefs=[],this._phaseSeq=[],this._requiredCompletion=[],this._scheduleTime="",this._scheduleTimeOn=!1,this._environmentalEntity="",this._environmentalAttribute="",this._environmentalInitial="",this._environmentalAttributeInitial="",this._adaptiveEnabled=!1,this._adaptiveAlpha=oe.alpha,this._adaptiveMin=oe.min,this._adaptiveMax=oe.max,this._adaptiveSeasonal=!0,this._adaptivePrediction=!0,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=!1,this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1,this._actionTesting=!1,this._actionTestResult="",this._qcNotes="",this._qcCost="",this._qcDuration="",this._qcFeedback="",this._resetTriggerFields()}_typeFields(){return{type:this._triggerType,attribute:this._triggerAttribute,above:this._triggerAbove,below:this._triggerBelow,equals:this._triggerEquals,notEquals:this._triggerNotEquals,forMinutes:this._triggerForMinutes,targetValue:this._triggerTargetValue,deltaMode:this._triggerDeltaMode,fromState:this._triggerFromState,toState:this._triggerToState,targetChanges:this._triggerTargetChanges,runtimeHours:this._triggerRuntimeHours,onStates:this._triggerOnStates,daysBefore:this._triggerDaysBefore}}_setTypeFields(e){this._triggerType=e.type,this._triggerAttribute=e.attribute,this._triggerAbove=e.above,this._triggerBelow=e.below,this._triggerEquals=e.equals,this._triggerNotEquals=e.notEquals,this._triggerForMinutes=e.forMinutes,this._triggerTargetValue=e.targetValue,this._triggerDeltaMode=e.deltaMode,this._triggerFromState=e.fromState,this._triggerToState=e.toState,this._triggerTargetChanges=e.targetChanges,this._triggerRuntimeHours=e.runtimeHours,this._triggerOnStates=e.onStates,this._triggerDaysBefore=e.daysBefore}_resetTriggerFields(){this._triggerEntityId="",this._triggerEntityIds=[],this._triggerEntityLogic="any",this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain="",this._setTypeFields(Os()),this._triggerCombinator="any",this._triggerBaselineValue="",this._liveBaselineValue=null,this._autoCompleteOnRecovery=!1,this._triggerRuntimeMaxSession="",this._compoundLogic="AND",this._compoundConditions=[]}async _loadUsers(){this._userService||(this._userService=new Gt(this.hass));try{this._availableUsers=await this._userService.getUsers()}catch(e){console.error("Failed to load users:",e),this._availableUsers=[]}}_toggleAssignee(e){this._assigneePool=this._assigneePool.includes(e)?this._assigneePool.filter(t=>t!==e):[...this._assigneePool,e]}async _testAction(){let e=this._actionService.trim();if(!e||!/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(e)){this._actionTestResult="error",this._actionTestError=n("action_test_bad_format",this._lang),setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},5e3);return}let[t,i]=e.split(".");if(!this.hass?.services?.[t]?.[i]){this._actionTestResult="error",this._actionTestError=n("action_test_unknown_service",this._lang).replace("{service}",e),setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},8e3);return}let a=this._actionTargetEntity.trim();if(a){let o=a.split(".")[0];if(o!==t&&!new Set(["homeassistant","scene","notify","persistent_notification"]).has(t)){this._actionTestResult="error",this._actionTestError=n("action_test_domain_mismatch",this._lang).replace("{service}",e).replace("{domain}",t).replace("{entity}",a).replace("{entity_domain}",o).replace("{suggestion}",`${o}.${i}`),setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},8e3);return}if(!this.hass.states?.[a]){this._actionTestResult="error",this._actionTestError=n("action_test_entity_missing",this._lang).replace("{entity}",a),setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},8e3);return}}this._actionTestResult="ok",setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},5e3)}_buildActionData(){if(this._actionDataJsonFallback.trim())try{let e=JSON.parse(this._actionDataJsonFallback);if(e&&typeof e=="object"&&!Array.isArray(e))return e}catch{}return{...this._actionData}}_serviceSchema(){let e=this._actionService.trim();if(!e||!/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(e))return null;let[t,i]=e.split("."),a=this.hass?.services?.[t]?.[i]?.fields;return!a||Object.keys(a).length===0?null:Object.entries(a).map(([o,d])=>({name:o,required:!!d.required,selector:d.selector||{text:{}}}))}_actionTargetExtended(){let e=this._actionTargetStored;return e?Object.entries(e).some(([t,i])=>t==="entity_id"?Array.isArray(i)&&i.length>1:Array.isArray(i)?i.length>0:!!i):!1}_actionTargetSummary(){let e=this._actionTargetStored||{},t=a=>(Array.isArray(a)?a:[a]).filter(o=>typeof o=="string"&&!!o),i=this.hass;return[...t(e.entity_id).map(a=>i.states?.[a]?.attributes?.friendly_name||a),...t(e.device_id).map(a=>i.devices?.[a]?.name_by_user||i.devices?.[a]?.name||a),...t(e.area_id).map(a=>i.areas?.[a]?.name||a),...t(e.floor_id).map(a=>i.floors?.[a]?.name||a),...t(e.label_id)].join(", ")}_renderCompletionActionsSection(e){if(!this.completionActionsEnabled&&!this._actionPresent)return h;let t=this._serviceSchema();return l` + `],c([v()],ke.prototype,"value",2),c([v()],ke.prototype,"lang",2),c([u()],ke.prototype,"_credit",2),c([u()],ke.prototype,"_amount",2);customElements.get("ms-cost-input")||customElements.define("ms-cost-input",ke)});function on(){return{entityIds:"",type:"threshold",attribute:"",above:"",below:"",equals:"",notEquals:"",forMinutes:"0",targetValue:"",deltaMode:!1,fromState:"",toState:"",targetChanges:"",runtimeHours:"",onStates:"",daysBefore:"0",carry:{}}}function cn(s){return{type:s.type||"threshold",attribute:s.attribute||"",above:s.trigger_above?.toString()??"",below:s.trigger_below?.toString()??"",equals:s.trigger_equals?.toString()??"",notEquals:s.trigger_not_equals?.toString()??"",forMinutes:s.trigger_for_minutes?.toString()??"0",targetValue:s.trigger_target_value?.toString()??"",deltaMode:s.trigger_delta_mode||!1,fromState:s.trigger_from_state||"",toState:s.trigger_to_state||"",targetChanges:s.trigger_target_changes?.toString()??"",runtimeHours:s.trigger_runtime_hours?.toString()??"",onStates:(s.trigger_on_states||[]).join(", "),daysBefore:s.trigger_days_before?.toString()??"0"}}function dn(s,r){r.attribute&&(s.attribute=r.attribute);let e=parseInt(r.forMinutes,10);if(r.type==="threshold"){let t=parseFloat(r.above);isNaN(t)||(s.trigger_above=t);let i=parseFloat(r.below);isNaN(i)||(s.trigger_below=i);let a=parseFloat(r.equals);isNaN(a)||(s.trigger_equals=a);let o=parseFloat(r.notEquals);isNaN(o)||(s.trigger_not_equals=o),isNaN(e)||(s.trigger_for_minutes=e)}else if(r.type==="counter"){let t=parseFloat(r.targetValue);isNaN(t)||(s.trigger_target_value=t),s.trigger_delta_mode=r.deltaMode}else if(r.type==="state_change"){r.fromState&&(s.trigger_from_state=r.fromState),r.toState&&(s.trigger_to_state=r.toState);let t=parseInt(r.targetChanges,10);isNaN(t)||(s.trigger_target_changes=t),isNaN(e)||(s.trigger_for_minutes=e)}else if(r.type==="runtime"){let t=parseFloat(r.runtimeHours);isNaN(t)||(s.trigger_runtime_hours=t);let i=(r.onStates||"").split(",").map(a=>a.trim()).filter(Boolean);i.length>0&&(s.trigger_on_states=i)}else if(r.type==="due_date"){let t=parseInt(r.daysBefore,10);s.trigger_days_before=isNaN(t)?0:t}}function yo(s){return{entityIds:(s.entity_ids||(s.entity_id?[s.entity_id]:[])).join(", "),...cn(s),carry:Object.fromEntries(Object.entries(s).filter(([e])=>!vo.has(e)&&!e.startsWith("_")))}}function bo(s){let r=s.entityIds.split(",").map(t=>t.trim()).filter(Boolean);if(r.length===0)return null;let e={...s.carry||{},entity_id:r[0],entity_ids:r,type:s.type};return dn(e,s),e}function xo(s){return Array.from({length:7},(r,e)=>Ke(e,s,"short"))}function wo(s){return Array.from({length:12},(r,e)=>Fi(e,s,"short"))}var _o,mo,go,ni,nn,ln,an,fo,de,Me,vo,g,Ji,pn=y(()=>{"use strict";I();F();T();Qs();ii();Oe();rn();De();Et();ri();Qi();Gi();sn();Xe();si();_o=["cleaning","inspection","replacement","calibration","service","reading","custom"],mo=["low","normal","high"],go=["time_based","weekdays","nth_weekday","day_of_month","calendar","sensor_based","one_time","manual"],ni=["weekdays","nth_weekday","day_of_month","calendar"],nn=["time_based","one_time",...ni],ln=["threshold","counter","state_change","runtime","due_date"],an=(s,r)=>n(s==="due_date"?"trigger_type_due_date":s,r),fo=[...ln,"compound"],de={alpha:"0.3",min:"7",max:"365"},Me=we;vo=new Set(["entity_id","entity_ids","type","attribute","trigger_above","trigger_below","trigger_equals","trigger_not_equals","trigger_for_minutes","trigger_target_value","trigger_delta_mode","trigger_from_state","trigger_to_state","trigger_target_changes","trigger_runtime_hours","trigger_on_states","trigger_days_before"]);g=class g extends S{constructor(){super(...arguments);this.checklistsEnabled=!1;this.scheduleTimeEnabled=!1;this.completionActionsEnabled=!1;this.adaptiveFeature=!1;this.seasonalFeature=!1;this.environmentalFeature=!1;this.defaultWarningDays=7;this.parts=[];this._foreignOwners=[];this._open=!1;this._entityPickerFallback=!1;this._pickerProbeStrikes=0;this._loading=!1;this._error="";this._warning="";this._entryId="";this._taskId=null;this._isFleetTask=!1;this._objectChoices=[];this._name="";this._type="custom";this._scheduleType="time_based";this._intervalDays="30";this._intervalUnit="days";this._dueDate="";this._warningDays="7";this._earliestCompletionDays="";this._intervalAnchor="completion";this._weekdays=[];this._nth="1";this._nthWeekday="5";this._domDay="1";this._domLastDay=!1;this._domBusiness=!1;this._calOffset="0";this._calendarEntity="";this._seasonMonths=[];this._endsMode="never";this._endsCount="";this._endsUntil="";this._schedulePreview=[];this._schedulePreviewEnded=!1;this._previewSeq=0;this._notes="";this._documentationUrl="";this._customIcon="";this._notifyIcon="";this._priority="normal";this._labels="";this._mirrorTodoEntities=[];this._enabled=!0;this._triggerEntityId="";this._triggerEntityIds=[];this._triggerEntityLogic="any";this._triggerAttribute="";this._triggerType="threshold";this._triggerAbove="";this._triggerBelow="";this._triggerEquals="";this._triggerNotEquals="";this._triggerForMinutes="0";this._triggerCombinator="any";this._triggerTargetValue="";this._triggerDeltaMode=!1;this._triggerBaselineValue="";this._liveBaselineValue=null;this._autoCompleteOnRecovery=!1;this._triggerFromState="";this._triggerToState="";this._triggerTargetChanges="";this._triggerRuntimeHours="";this._triggerDaysBefore="0";this._triggerRuntimeMaxSession="";this._triggerOnStates="";this._compoundLogic="AND";this._compoundConditions=[];this._suggestedAttributes=[];this._availableAttributes=[];this._entityDomain="";this._lastPerformed="";this._nfcTagId="";this._requireTagScan=!1;this._allowSkip=!0;this._notifyEnabled=!0;this._readingUnit="";this._readings=[];this._consumesParts={};this._consumesQtyText={};this._partsLoadFailed=!1;this._availableTags=[];this._responsibleUserId=null;this._loadedLastPerformed="";this._loadedResponsibleUserId=null;this._assigneePool=[];this._rotationStrategy="";this._availableUsers=[];this._checklistText="";this._phaseDefs=[];this._phaseSeq=[];this._requiredCompletion=[];this._scheduleTime="";this._scheduleTimeOn=!1;this._actionService="";this._actionTargetEntity="";this._actionTargetStored=null;this._actionTargetInitial="";this._actionSkipAuto=!1;this._actionPresent=!1;this._actionData={};this._actionDataJsonFallback="";this._actionTesting=!1;this._actionTestResult="";this._actionTestError="";this._qcNotes="";this._qcCost="";this._qcDuration="";this._qcFeedback="";this._environmentalEntity="";this._environmentalAttribute="";this._environmentalInitial="";this._environmentalAttributeInitial="";this._adaptiveEnabled=!1;this._adaptiveAlpha=de.alpha;this._adaptiveMin=de.min;this._adaptiveMax=de.max;this._adaptiveSeasonal=!0;this._adaptivePrediction=!0;this._adaptiveInitial="";this._adaptiveWasEnabled=!1;this._userService=null;this._conditionAttrOptions={};this._conditionAttrPending=new Set}_adaptiveSnapshot(){return JSON.stringify([this._adaptiveEnabled,this._adaptiveAlpha,this._adaptiveMin,this._adaptiveMax,this._adaptiveSeasonal,this._adaptivePrediction])}get _lang(){return N(this.hass)}async openCreate(e,t){this._entryId=e,this._taskId=null,this._isFleetTask=!1,this._error="",this._warning="",!e&&t&&t.length>0?(this._objectChoices=t.map(i=>({entry_id:i.entry_id,name:i.object.name})).sort((i,a)=>i.name.localeCompare(a.name)),this._entryId=this._objectChoices[0].entry_id):this._objectChoices=[],this._resetFields(),await Promise.all([this._loadUsers(),this._loadTags(),this._loadParts(),this._loadForeignPools()]),this._open=!0}async openEdit(e,t){this._entryId=e,this._taskId=t.id,this._error="",this._warning="",this._objectChoices=[],this._isFleetTask=t.battery_fleet_task===!0,this._name=t.name,this._type=t.type,this._scheduleType=t.schedule_type,this._intervalDays=t.interval_days!=null?String(t.interval_days):"",this._intervalUnit=t.interval_unit||"days",this._dueDate=t.due_date||"";let i=t.schedule;this._weekdays=i?.kind==="weekdays"?[...i.weekdays??[]]:[],this._nth=i?.kind==="nth_weekday"?String(i.nth??1):"1",this._nthWeekday=i?.kind==="nth_weekday"?String(i.weekday??5):"5",this._domDay=i?.kind==="day_of_month"&&(i.day??1)>=1?String(i.day??1):"1",this._domLastDay=i?.kind==="day_of_month"&&i.day===-1,this._domBusiness=i?.kind==="day_of_month"&&i.business===!0,this._calendarEntity=i?.kind==="calendar"&&i.entity_id||"",this._calOffset=i?.offset?String(i.offset):"0",this._seasonMonths=Array.isArray(i?.season_months)?[...i.season_months]:[];let a=i?.ends;a&&typeof a.count=="number"?(this._endsMode="count",this._endsCount=String(a.count),this._endsUntil=""):a&&typeof a.until=="string"?(this._endsMode="until",this._endsUntil=a.until,this._endsCount=""):(this._endsMode="never",this._endsCount="",this._endsUntil=""),this._warningDays=t.warning_days.toString(),this._earliestCompletionDays=t.earliest_completion_days!=null?String(t.earliest_completion_days):"",this._intervalAnchor=t.interval_anchor||"completion",this._notes=t.notes||"",this._documentationUrl=t.documentation_url||"",this._customIcon=t.custom_icon||"",this._notifyIcon=t.notify_icon||"",this._priority=t.priority||"normal",this._labels=(t.labels||[]).join(", "),this._mirrorTodoEntities=[...t.mirror_todo_entities||[]],this._enabled=t.enabled!==!1,this._lastPerformed=t.last_performed||"",this._loadedLastPerformed=this._lastPerformed,this._nfcTagId=t.nfc_tag_id||"",this._requireTagScan=!!t.require_tag_scan,this._allowSkip=t.allow_skip!==!1,this._notifyEnabled=t.notify_enabled!==!1,this._readingUnit=t.reading_unit||"",this._readings=(t.readings||[]).map(_=>({..._})),this._consumesParts=Object.fromEntries((t.consumes_parts||[]).map(_=>[ae(_),{..._}])),this._consumesQtyText={},this._responsibleUserId=t.responsible_user_id||null,this._loadedResponsibleUserId=this._responsibleUserId,this._assigneePool=[...t.assignee_pool||[]],this._rotationStrategy=t.rotation_strategy||"",this._checklistText=(t.checklist||[]).join(` +`),this._phaseDefs=Object.entries(t.phases||{}).map(([_,m])=>{let{name:f,checklist:x,consumes_parts:w,required_completion_fields:k,...q}=m,L=m.consumes_parts||[],M=L.findIndex(W=>!W.entry_id),B=M>=0?L[M]:void 0;return{id:_,name:m.name||_,checklistText:(m.checklist||[]).join(` +`),partId:B?.part_id||"",partQty:B?.quantity!=null?String(B.quantity):"",reqOverride:m.required_completion_fields!==void 0,reqFields:[...m.required_completion_fields||[]],extraParts:L.filter((W,b)=>b!==M).map(W=>({...W})),carry:q}}),this._phaseSeq=[...t.phase_sequence||[]],this._requiredCompletion=[...t.required_completion_fields||[]],this._scheduleTime=t.schedule_time||"",this._scheduleTimeOn=!!t.schedule_time;let o=t.on_complete_action;if(o&&o.service){this._actionService=o.service;let _=o.target?.entity_id;this._actionTargetEntity=Array.isArray(_)?_[0]||"":_||"",this._actionTargetStored=o.target&&typeof o.target=="object"?JSON.parse(JSON.stringify(o.target)):null,this._actionTargetInitial=this._actionTargetEntity,this._actionData=o.data&&typeof o.data=="object"?{...o.data}:{},this._actionDataJsonFallback="",this._actionSkipAuto=o.skip_auto===!0,this._actionPresent=!0}else this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1;let d=t.quick_complete_defaults;this._qcNotes=d?.notes||"",this._qcCost=d?.cost!=null?String(d.cost):"",this._qcDuration=d?.duration!=null?String(d.duration):"",this._qcFeedback=d?.feedback||"";let p=t.adaptive_config||{};if(this._environmentalEntity=p.environmental_entity||"",this._environmentalAttribute=p.environmental_attribute||"",this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute,this._adaptiveEnabled=!!p.enabled,this._adaptiveAlpha=p.ewa_alpha?.toString()??de.alpha,this._adaptiveMin=p.min_interval_days?.toString()??de.min,this._adaptiveMax=p.max_interval_days?.toString()??de.max,this._adaptiveSeasonal=p.seasonal_enabled!==!1,this._adaptivePrediction=p.sensor_prediction_enabled!==!1,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=this._adaptiveEnabled,t.trigger_config){let _=t.trigger_config;this._triggerEntityId=_.entity_id||_.entity_ids&&_.entity_ids[0]||"",this._triggerEntityIds=_.entity_ids||(_.entity_id?[_.entity_id]:[]),this._triggerEntityLogic=_.entity_logic||"any",this._setTypeFields(cn(_)),this._triggerCombinator=_.trigger_combinator==="all"?"all":"any",this._triggerBaselineValue=_.trigger_baseline_value?.toString()||"",this._liveBaselineValue=t.trigger_baseline_value??null,this._autoCompleteOnRecovery=_.auto_complete_on_recovery||!1,this._triggerRuntimeMaxSession=_.trigger_runtime_max_session_seconds?.toString()||"",_.type==="compound"?(this._compoundLogic=_.compound_logic==="OR"?"OR":"AND",this._compoundConditions=(_.conditions||[]).map(yo)):(this._compoundLogic="AND",this._compoundConditions=[])}else this._resetTriggerFields();this._triggerEntityId&&this._fetchEntityAttributes(this._triggerEntityId),await Promise.all([this._loadUsers(),this._loadTags(),this._loadParts(),this._loadForeignPools()]),this._open=!0}_resetFields(){this._name="",this._type="custom",this._scheduleType="time_based",this._intervalDays="30",this._intervalUnit="days",this._dueDate="",this._warningDays=String(this.defaultWarningDays),this._earliestCompletionDays="",this._intervalAnchor="completion",this._weekdays=[],this._nth="1",this._nthWeekday="5",this._domDay="1",this._domLastDay=!1,this._domBusiness=!1,this._calOffset="0",this._seasonMonths=[],this._endsMode="never",this._endsCount="",this._endsUntil="",this._notes="",this._documentationUrl="",this._customIcon="",this._notifyIcon="",this._priority="normal",this._labels="",this._mirrorTodoEntities=[],this._enabled=!0,this._lastPerformed="",this._nfcTagId="",this._requireTagScan=!1,this._allowSkip=!0,this._readingUnit="",this._readings=[],this._consumesParts={},this._consumesQtyText={},this._responsibleUserId=null,this._assigneePool=[],this._rotationStrategy="",this._checklistText="",this._phaseDefs=[],this._phaseSeq=[],this._requiredCompletion=[],this._scheduleTime="",this._scheduleTimeOn=!1,this._environmentalEntity="",this._environmentalAttribute="",this._environmentalInitial="",this._environmentalAttributeInitial="",this._adaptiveEnabled=!1,this._adaptiveAlpha=de.alpha,this._adaptiveMin=de.min,this._adaptiveMax=de.max,this._adaptiveSeasonal=!0,this._adaptivePrediction=!0,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=!1,this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1,this._actionTesting=!1,this._actionTestResult="",this._qcNotes="",this._qcCost="",this._qcDuration="",this._qcFeedback="",this._resetTriggerFields()}_typeFields(){return{type:this._triggerType,attribute:this._triggerAttribute,above:this._triggerAbove,below:this._triggerBelow,equals:this._triggerEquals,notEquals:this._triggerNotEquals,forMinutes:this._triggerForMinutes,targetValue:this._triggerTargetValue,deltaMode:this._triggerDeltaMode,fromState:this._triggerFromState,toState:this._triggerToState,targetChanges:this._triggerTargetChanges,runtimeHours:this._triggerRuntimeHours,onStates:this._triggerOnStates,daysBefore:this._triggerDaysBefore}}_setTypeFields(e){this._triggerType=e.type,this._triggerAttribute=e.attribute,this._triggerAbove=e.above,this._triggerBelow=e.below,this._triggerEquals=e.equals,this._triggerNotEquals=e.notEquals,this._triggerForMinutes=e.forMinutes,this._triggerTargetValue=e.targetValue,this._triggerDeltaMode=e.deltaMode,this._triggerFromState=e.fromState,this._triggerToState=e.toState,this._triggerTargetChanges=e.targetChanges,this._triggerRuntimeHours=e.runtimeHours,this._triggerOnStates=e.onStates,this._triggerDaysBefore=e.daysBefore}_resetTriggerFields(){this._triggerEntityId="",this._triggerEntityIds=[],this._triggerEntityLogic="any",this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain="",this._setTypeFields(on()),this._triggerCombinator="any",this._triggerBaselineValue="",this._liveBaselineValue=null,this._autoCompleteOnRecovery=!1,this._triggerRuntimeMaxSession="",this._compoundLogic="AND",this._compoundConditions=[]}async _loadUsers(){this._userService||(this._userService=new ti(this.hass));try{this._availableUsers=await this._userService.getUsers()}catch(e){console.error("Failed to load users:",e),this._availableUsers=[]}}_toggleAssignee(e){this._assigneePool=this._assigneePool.includes(e)?this._assigneePool.filter(t=>t!==e):[...this._assigneePool,e]}async _testAction(){let e=this._actionService.trim();if(!e||!/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(e)){this._actionTestResult="error",this._actionTestError=n("action_test_bad_format",this._lang),setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},5e3);return}let[t,i]=e.split(".");if(!this.hass?.services?.[t]?.[i]){this._actionTestResult="error",this._actionTestError=n("action_test_unknown_service",this._lang).replace("{service}",e),setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},8e3);return}let a=this._actionTargetEntity.trim();if(a){let o=a.split(".")[0];if(o!==t&&!new Set(["homeassistant","scene","notify","persistent_notification"]).has(t)){this._actionTestResult="error",this._actionTestError=n("action_test_domain_mismatch",this._lang).replace("{service}",e).replace("{domain}",t).replace("{entity}",a).replace("{entity_domain}",o).replace("{suggestion}",`${o}.${i}`),setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},8e3);return}if(!this.hass.states?.[a]){this._actionTestResult="error",this._actionTestError=n("action_test_entity_missing",this._lang).replace("{entity}",a),setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},8e3);return}}this._actionTestResult="ok",setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},5e3)}_buildActionData(){if(this._actionDataJsonFallback.trim())try{let e=JSON.parse(this._actionDataJsonFallback);if(e&&typeof e=="object"&&!Array.isArray(e))return e}catch{}return{...this._actionData}}_serviceSchema(){let e=this._actionService.trim();if(!e||!/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(e))return null;let[t,i]=e.split("."),a=this.hass?.services?.[t]?.[i]?.fields;return!a||Object.keys(a).length===0?null:Object.entries(a).map(([o,d])=>({name:o,required:!!d.required,selector:d.selector||{text:{}}}))}_actionTargetExtended(){let e=this._actionTargetStored;return e?Object.entries(e).some(([t,i])=>t==="entity_id"?Array.isArray(i)&&i.length>1:Array.isArray(i)?i.length>0:!!i):!1}_actionTargetSummary(){let e=this._actionTargetStored||{},t=a=>(Array.isArray(a)?a:[a]).filter(o=>typeof o=="string"&&!!o),i=this.hass;return[...t(e.entity_id).map(a=>i.states?.[a]?.attributes?.friendly_name||a),...t(e.device_id).map(a=>i.devices?.[a]?.name_by_user||i.devices?.[a]?.name||a),...t(e.area_id).map(a=>i.areas?.[a]?.name||a),...t(e.floor_id).map(a=>i.floors?.[a]?.name||a),...t(e.label_id)].join(", ")}_renderCompletionActionsSection(e){if(!this.completionActionsEnabled&&!this._actionPresent)return h;let t=this._serviceSchema();return l`
${n("on_complete_action_title",e)}

${n("on_complete_action_desc",e)}

@@ -1767,27 +1767,27 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
`:h} - `}async _loadParts(){if(this.parts=[],!!this._entryId)try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this.parts=e.parts||[],this._partsLoadFailed=!1}catch{this.parts=[],this._partsLoadFailed=!0}}async _loadForeignPools(){if(this._foreignOwners=[],!!this._entryId)try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"});this._foreignOwners=(e.objects||[]).filter(t=>t.entry_id!==this._entryId&&(t.parts||[]).length>0).map(t=>({entry_id:t.entry_id,name:t.object?.name||t.entry_id,parts:t.parts||[]})).sort((t,i)=>t.name.localeCompare(i.name))}catch{this._foreignOwners=[]}}async _loadTags(){try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tags/list"});this._availableTags=e.tags||[]}catch{this._availableTags=[]}}_fetchConditionAttributes(e){!e||!this.hass||this._conditionAttrOptions[e]||this._conditionAttrPending.has(e)||(this._conditionAttrPending.add(e),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/entity/attributes",entity_id:e}).then(t=>{let i=t;this._conditionAttrOptions={...this._conditionAttrOptions,[e]:{suggested:i.suggested_attributes||[],available:i.available_attributes||[]}}}).catch(()=>{this._conditionAttrOptions={...this._conditionAttrOptions,[e]:{suggested:[],available:[]}}}))}async _fetchEntityAttributes(e){if(!e||!this.hass){this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain="";return}try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/entity/attributes",entity_id:e});this._entityDomain=t.domain||"",this._suggestedAttributes=t.suggested_attributes||[],this._availableAttributes=t.available_attributes||[]}catch{this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain=""}}get _hasForeignPick(){return Object.values(this._consumesParts).some(e=>!!e.entry_id)}_renderConsumesRow(e,t){let i=re({part_id:e.id,entry_id:t}),a=this._consumesParts[i],o=t?{part_id:e.id,quantity:1,entry_id:t}:{part_id:e.id,quantity:1};return l` + `}async _loadParts(){if(this.parts=[],!!this._entryId)try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this.parts=e.parts||[],this._partsLoadFailed=!1}catch{this.parts=[],this._partsLoadFailed=!0}}async _loadForeignPools(){if(this._foreignOwners=[],!!this._entryId)try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"});this._foreignOwners=(e.objects||[]).filter(t=>t.entry_id!==this._entryId&&(t.parts||[]).length>0).map(t=>({entry_id:t.entry_id,name:t.object?.name||t.entry_id,parts:t.parts||[]})).sort((t,i)=>t.name.localeCompare(i.name))}catch{this._foreignOwners=[]}}async _loadTags(){try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tags/list"});this._availableTags=e.tags||[]}catch{this._availableTags=[]}}_fetchConditionAttributes(e){!e||!this.hass||this._conditionAttrOptions[e]||this._conditionAttrPending.has(e)||(this._conditionAttrPending.add(e),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/entity/attributes",entity_id:e}).then(t=>{let i=t;this._conditionAttrOptions={...this._conditionAttrOptions,[e]:{suggested:i.suggested_attributes||[],available:i.available_attributes||[]}}}).catch(()=>{this._conditionAttrOptions={...this._conditionAttrOptions,[e]:{suggested:[],available:[]}}}))}async _fetchEntityAttributes(e){if(!e||!this.hass){this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain="";return}try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/entity/attributes",entity_id:e});this._entityDomain=t.domain||"",this._suggestedAttributes=t.suggested_attributes||[],this._availableAttributes=t.available_attributes||[]}catch{this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain=""}}get _hasForeignPick(){return Object.values(this._consumesParts).some(e=>!!e.entry_id)}_renderConsumesRow(e,t){let i=ae({part_id:e.id,entry_id:t}),a=this._consumesParts[i],o=t?{part_id:e.id,quantity:1,entry_id:t}:{part_id:e.id,quantity:1};return l`
${a!==void 0?l`{let u=d.target.value;this._consumesQtyText={...this._consumesQtyText,[i]:u};let _=parseFloat(u.replace(",","."));Number.isFinite(_)&&_>=Le[0]&&(this._consumesParts={...this._consumesParts,[i]:{...o,quantity:_}})}} + @input=${d=>{let p=d.target.value;this._consumesQtyText={...this._consumesQtyText,[i]:p};let _=parseFloat(p.replace(",","."));Number.isFinite(_)&&_>=Me[0]&&(this._consumesParts={...this._consumesParts,[i]:{...o,quantity:_}})}} />`:h}
- `}_applyConsumesQtyText(){let e={...this._consumesParts};for(let t of Object.keys(e)){let i=this._consumesQtyText[t];if(i===void 0)continue;let a=parseFloat(i.replace(",","."));if(!Number.isFinite(a)||aLe[1]){let o=d=>I(d,this._lang,{maximumFractionDigits:2});return this._error=n("settings_value_out_of_range",this._lang).replace("{min}",o(Le[0])).replace("{max}",o(Le[1])),!1}e[t]={...e[t],quantity:a}}return this._consumesParts=e,!0}_toggleRequired(e,t){let i=new Set(this._requiredCompletion);t?i.add(e):i.delete(e),this._requiredCompletion=[...i]}_patchReading(e,t){this._readings=this._readings.map(i=>i.id===e?{...i,...t}:i)}_renderReadingsEditor(e){let t=ps(this._readings);return l` + `}_applyConsumesQtyText(){let e={...this._consumesParts};for(let t of Object.keys(e)){let i=this._consumesQtyText[t];if(i===void 0)continue;let a=parseFloat(i.replace(",","."));if(!Number.isFinite(a)||aMe[1]){let o=d=>C(d,this._lang,{maximumFractionDigits:2});return this._error=n("settings_value_out_of_range",this._lang).replace("{min}",o(Me[0])).replace("{max}",o(Me[1])),!1}e[t]={...e[t],quantity:a}}return this._consumesParts=e,!0}_toggleRequired(e,t){let i=new Set(this._requiredCompletion);t?i.add(e):i.delete(e),this._requiredCompletion=[...i]}_patchReading(e,t){this._readings=this._readings.map(i=>i.id===e?{...i,...t}:i)}_renderReadingsEditor(e){let t=Ls(this._readings);return l`
${n("readings_section",e)}
${n("readings_hint",e)}
@@ -1811,9 +1811,9 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
`)} - ${this._readings.length<20?l` + ${this._readings.lengththis._readings=[...this._readings,{id:as(),name:"",unit:this._readings.length?this._readings[this._readings.length-1].unit:this._readingUnit}]}> + @click=${()=>this._readings=[...this._readings,{id:As(),name:"",unit:this._readings.length?this._readings[this._readings.length-1].unit:this._readingUnit}]}> ${n("reading_add",e)} `:h}
@@ -1841,7 +1841,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" - this._patchPhaseDef(i.id,{partQty:a.target.value})} /> `:h} `:h} @@ -1868,14 +1868,14 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" ${i.reqOverride?l`
- ${Li.map(a=>l` + ${Yi.map(a=>l` `)}
@@ -1904,9 +1904,9 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
`:h} - `}async _save(){if(!this._loading&&this._name.trim()){if(this._adaptiveSnapshot()!==this._adaptiveInitial){let e=parseInt(this._adaptiveMin,10),t=parseInt(this._adaptiveMax,10);if(!isNaN(e)&&!isNaN(t)&&e>t){this._error=`${n("adaptive_min_interval",this._lang)} > ${n("adaptive_max_interval",this._lang)}`;return}}if(this._triggerType==="threshold"&&this._thresholdLimitsOverlap()){this._error=n("trigger_hint_overlap",this._lang);return}if(this._scheduleType==="calendar"&&!this._calendarEntity.trim().startsWith("calendar.")){this._error=n("calendar_entity_required",this._lang);return}if(this._applyConsumesQtyText()){this._loading=!0,this._error="";try{let e=await j(this,()=>this.hass.connection.sendMessagePromise(this._savePayload()),{fallbackKey:"save_error",onError:o=>{this._error=o}});if(e===void 0)return;let t=this._taskId||e?.task_id,i=this._environmentalEntity!==this._environmentalInitial||this._environmentalAttribute!==this._environmentalAttributeInitial;this._warning="";let a=o=>{this._warning=n("subsave_warning",this._lang).replace("{detail}",o)};if(t&&this._scheduleType==="sensor_based"&&i&&await j(this,{type:"maintenance_supporter/task/set_environmental_entity",entry_id:this._entryId,task_id:t,environmental_entity:this._environmentalEntity||null,environmental_attribute:this._environmentalAttribute||null},{onError:a})!==void 0&&(this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute),t&&this._adaptiveSnapshot()!==this._adaptiveInitial){let o=parseFloat(this._adaptiveAlpha),d=parseInt(this._adaptiveMin,10),u=parseInt(this._adaptiveMax,10);await j(this,{type:"maintenance_supporter/task/set_adaptive",entry_id:this._entryId,task_id:t,enabled:this._adaptiveEnabled,...o>=ft[0]&&o<=ft[1]?{ewa_alpha:o}:{},...!isNaN(d)&&d>=1?{min_interval_days:d}:{},...!isNaN(u)&&u>=1?{max_interval_days:u}:{},seasonal_enabled:this._adaptiveSeasonal,sensor_prediction_enabled:this._adaptivePrediction},{onError:a})!==void 0&&(this._adaptiveInitial=this._adaptiveSnapshot())}this._warning?t&&(this._taskId=t):this._open=!1,this.dispatchEvent(new CustomEvent("task-saved"))}finally{this._loading=!1}}}}_savePayload(){let e={type:this._taskId?"maintenance_supporter/task/update":"maintenance_supporter/task/create",entry_id:this._entryId,name:this._name,task_type:this._type,schedule_type:this._scheduleType,warning_days:Number.isNaN(parseInt(this._warningDays,10))?this.defaultWarningDays:Math.max(0,parseInt(this._warningDays,10))},t=this._earliestCompletionDays.trim();e.earliest_completion_days=t===""?null:Math.max(0,parseInt(t,10)||0),this._taskId&&(e.task_id=this._taskId),this._scheduleType==="one_time"?(e.due_date=this._dueDate||null,e.interval_days=null):Jt.includes(this._scheduleType)?(e.schedule={...this._buildSchedule(),...this._recurrenceExtras()},e.interval_days=null,this._taskId&&(e.due_date=null)):(this._taskId&&(e.due_date=null),this._scheduleType!=="manual"&&this._intervalDays?(e.interval_days=parseInt(this._intervalDays,10),e.interval_unit=this._intervalUnit,e.interval_anchor=this._intervalAnchor,this._scheduleType==="time_based"&&(e.schedule={kind:"interval",...this._recurrenceExtras()})):this._taskId&&(e.interval_days=null,e.interval_anchor="completion")),e.notes=this._notes||null,e.documentation_url=this._documentationUrl||null,e.custom_icon=this._customIcon||null,e.notify_icon=this._notifyIcon.trim()||null,e.priority=this._priority,e.labels=this._labels.split(",").map(i=>i.trim()).filter(Boolean),e.mirror_todo_entities=this._mirrorTodoEntities.filter(Boolean),e.enabled=this._enabled,(!this._taskId||this._lastPerformed!==this._loadedLastPerformed)&&(e.last_performed=this._lastPerformed||null),e.nfc_tag_id=this._nfcTagId||null,e.require_tag_scan=this._requireTagScan,e.allow_skip=this._allowSkip,e.notify_enabled=this._notifyEnabled,e.reading_unit=this._readingUnit.trim()||null,e.readings=us(this._readings);{let i={};for(let o of this._phaseDefs){if(!o.name.trim())continue;let d={...o.carry,name:o.name.trim()},u=o.checklistText.split(` -`).map(m=>m.trim()).filter(Boolean);u.length&&(d.checklist=u);let _=[];if(o.partId){let m=parseFloat(o.partQty);_.push({part_id:o.partId,quantity:Number.isFinite(m)&&m>0?m:1})}for(let m of o.extraParts)_.push(m.entry_id?{part_id:m.part_id,quantity:m.quantity,entry_id:m.entry_id}:{part_id:m.part_id,quantity:m.quantity});_.length&&(d.consumes_parts=_),o.reqOverride&&(d.required_completion_fields=[...o.reqFields]),i[o.id]=d}let a=this._phaseSeq.filter(o=>o in i);e.phases=Object.keys(i).length&&a.length?i:null,e.phase_sequence=e.phases?a:null}if((this.parts.length||this._foreignOwners.length)&&(e.consumes_parts=Object.values(this._consumesParts).map(i=>i.entry_id?{part_id:i.part_id,quantity:i.quantity,entry_id:i.entry_id}:{part_id:i.part_id,quantity:i.quantity})),(!this._taskId||this._responsibleUserId!==this._loadedResponsibleUserId)&&(e.responsible_user_id=this._responsibleUserId),e.assignee_pool=this._assigneePool,e.required_completion_fields=this._requiredCompletion,e.rotation_strategy=this._assigneePool.length>=2&&this._rotationStrategy?this._rotationStrategy:null,this._scheduleType==="sensor_based"&&this._triggerType==="compound"){let i=this._compoundConditions.map(Ga).filter(a=>a!==null);if(i.length>0){let a={type:"compound",compound_logic:this._compoundLogic,conditions:i};this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),e.trigger_config=a}else this._taskId&&(e.trigger_config=null)}else if(this._scheduleType==="sensor_based"&&this._triggerEntityId){let i=this._triggerEntityIds.length>0?this._triggerEntityIds:[this._triggerEntityId],a={entity_id:i[0],entity_ids:i,type:this._triggerType};if(zs(a,this._typeFields()),this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),i.length>1&&(a.entity_logic=this._triggerEntityLogic),this._triggerType==="counter"&&this._triggerDeltaMode&&this._triggerBaselineValue){let o=parseFloat(this._triggerBaselineValue);!isNaN(o)&&o>=0&&(a.trigger_baseline_value=o)}else if(this._triggerType==="runtime"&&this._triggerRuntimeMaxSession){let o=parseInt(this._triggerRuntimeMaxSession,10);!isNaN(o)&&o>0&&(a.trigger_runtime_max_session_seconds=o)}e.trigger_config=a}else this._taskId&&(e.trigger_config=null);if(this.scheduleTimeEnabled&&Hs.includes(this._scheduleType)){let i=this._scheduleTimeOn?this._scheduleTime.trim():"";e.schedule_time=/^([01]\d|2[0-3]):[0-5]\d$/.test(i)?i:null}if(this.checklistsEnabled){let i=this._checklistText.split(` -`).map(a=>a.trim()).filter(Boolean).slice(0,100);e.checklist=i.length?i:null}if(this.completionActionsEnabled||this._actionPresent){let i=this._actionService.trim();if(i&&/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(i)){let a={service:i},o=this._actionTargetEntity.trim();this._actionTargetStored&&o===this._actionTargetInitial?a.target=JSON.parse(JSON.stringify(this._actionTargetStored)):o&&(a.target={entity_id:o});let d=this._buildActionData();Object.keys(d).length>0&&(a.data=d),this._actionSkipAuto&&(a.skip_auto=!0),e.on_complete_action=a}else e.on_complete_action=null}if(this.completionActionsEnabled){let i={};this._qcNotes.trim()&&(i.notes=this._qcNotes.trim());let a=parseFloat(this._qcCost);isNaN(a)||(i.cost=a);let o=Ke(this._qcDuration);o!==null&&(i.duration=o),this._qcFeedback&&(i.feedback=this._qcFeedback),e.quick_complete_defaults=Object.keys(i).length?i:null}return e}_close(){this._open=!1,this._warning="",this._pickerProbeTimer!==void 0&&(clearTimeout(this._pickerProbeTimer),this._pickerProbeTimer=void 0),this._pickerProbeStrikes=0}_renderTriggerFields(){if(this._scheduleType!=="sensor_based")return h;let e=this._lang,t=this._triggerType==="compound";return l` + `}async _save(){if(!this._loading&&this._name.trim()){if(this._adaptiveSnapshot()!==this._adaptiveInitial){let e=parseInt(this._adaptiveMin,10),t=parseInt(this._adaptiveMax,10);if(!isNaN(e)&&!isNaN(t)&&e>t){this._error=`${n("adaptive_min_interval",this._lang)} > ${n("adaptive_max_interval",this._lang)}`;return}}if(this._triggerType==="threshold"&&this._thresholdLimitsOverlap()){this._error=n("trigger_hint_overlap",this._lang);return}if(this._scheduleType==="calendar"&&!this._calendarEntity.trim().startsWith("calendar.")){this._error=n("calendar_entity_required",this._lang);return}if(this._applyConsumesQtyText()){this._loading=!0,this._error="";try{let e=await j(this,()=>this.hass.connection.sendMessagePromise(this._savePayload()),{fallbackKey:"save_error",onError:o=>{this._error=o}});if(e===void 0)return;let t=this._taskId||e?.task_id,i=this._environmentalEntity!==this._environmentalInitial||this._environmentalAttribute!==this._environmentalAttributeInitial;this._warning="";let a=o=>{this._warning=n("subsave_warning",this._lang).replace("{detail}",o)};if(t&&this._scheduleType==="sensor_based"&&i&&await j(this,{type:"maintenance_supporter/task/set_environmental_entity",entry_id:this._entryId,task_id:t,environmental_entity:this._environmentalEntity||null,environmental_attribute:this._environmentalAttribute||null},{onError:a})!==void 0&&(this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute),t&&this._adaptiveSnapshot()!==this._adaptiveInitial){let o=parseFloat(this._adaptiveAlpha),d=parseInt(this._adaptiveMin,10),p=parseInt(this._adaptiveMax,10);await j(this,{type:"maintenance_supporter/task/set_adaptive",entry_id:this._entryId,task_id:t,enabled:this._adaptiveEnabled,...o>=$t[0]&&o<=$t[1]?{ewa_alpha:o}:{},...!isNaN(d)&&d>=1?{min_interval_days:d}:{},...!isNaN(p)&&p>=1?{max_interval_days:p}:{},seasonal_enabled:this._adaptiveSeasonal,sensor_prediction_enabled:this._adaptivePrediction},{onError:a})!==void 0&&(this._adaptiveInitial=this._adaptiveSnapshot())}this._warning?t&&(this._taskId=t):this._open=!1,this.dispatchEvent(new CustomEvent("task-saved"))}finally{this._loading=!1}}}}_savePayload(){let e={type:this._taskId?"maintenance_supporter/task/update":"maintenance_supporter/task/create",entry_id:this._entryId,name:this._name,task_type:this._type,schedule_type:this._scheduleType,warning_days:Number.isNaN(parseInt(this._warningDays,10))?this.defaultWarningDays:Math.max(0,parseInt(this._warningDays,10))},t=this._earliestCompletionDays.trim();e.earliest_completion_days=t===""?null:Math.max(0,parseInt(t,10)||0),this._taskId&&(e.task_id=this._taskId),this._scheduleType==="one_time"?(e.due_date=this._dueDate||null,e.interval_days=null):ni.includes(this._scheduleType)?(e.schedule={...this._buildSchedule(),...this._recurrenceExtras()},e.interval_days=null,this._taskId&&(e.due_date=null)):(this._taskId&&(e.due_date=null),this._scheduleType!=="manual"&&this._intervalDays?(e.interval_days=parseInt(this._intervalDays,10),e.interval_unit=this._intervalUnit,e.interval_anchor=this._intervalAnchor,this._scheduleType==="time_based"&&(e.schedule={kind:"interval",...this._recurrenceExtras()})):this._taskId&&(e.interval_days=null,e.interval_anchor="completion")),e.notes=this._notes||null,e.documentation_url=this._documentationUrl||null,e.custom_icon=this._customIcon||null,e.notify_icon=this._notifyIcon.trim()||null,e.priority=this._priority,e.labels=this._labels.split(",").map(i=>i.trim()).filter(Boolean),e.mirror_todo_entities=this._mirrorTodoEntities.filter(Boolean),e.enabled=this._enabled,(!this._taskId||this._lastPerformed!==this._loadedLastPerformed)&&(e.last_performed=this._lastPerformed||null),e.nfc_tag_id=this._nfcTagId||null,e.require_tag_scan=this._requireTagScan,e.allow_skip=this._allowSkip,e.notify_enabled=this._notifyEnabled,e.reading_unit=this._readingUnit.trim()||null,e.readings=Ns(this._readings);{let i={};for(let o of this._phaseDefs){if(!o.name.trim())continue;let d={...o.carry,name:o.name.trim()},p=o.checklistText.split(` +`).map(m=>m.trim()).filter(Boolean);p.length&&(d.checklist=p);let _=[];if(o.partId){let m=parseFloat(o.partQty);_.push({part_id:o.partId,quantity:Number.isFinite(m)&&m>0?m:1})}for(let m of o.extraParts)_.push(m.entry_id?{part_id:m.part_id,quantity:m.quantity,entry_id:m.entry_id}:{part_id:m.part_id,quantity:m.quantity});_.length&&(d.consumes_parts=_),o.reqOverride&&(d.required_completion_fields=[...o.reqFields]),i[o.id]=d}let a=this._phaseSeq.filter(o=>o in i);e.phases=Object.keys(i).length&&a.length?i:null,e.phase_sequence=e.phases?a:null}if((this.parts.length||this._foreignOwners.length)&&(e.consumes_parts=Object.values(this._consumesParts).map(i=>i.entry_id?{part_id:i.part_id,quantity:i.quantity,entry_id:i.entry_id}:{part_id:i.part_id,quantity:i.quantity})),(!this._taskId||this._responsibleUserId!==this._loadedResponsibleUserId)&&(e.responsible_user_id=this._responsibleUserId),e.assignee_pool=this._assigneePool,e.required_completion_fields=this._requiredCompletion,e.rotation_strategy=this._assigneePool.length>=2&&this._rotationStrategy?this._rotationStrategy:null,this._scheduleType==="sensor_based"&&this._triggerType==="compound"){let i=this._compoundConditions.map(bo).filter(a=>a!==null);if(i.length>0){let a={type:"compound",compound_logic:this._compoundLogic,conditions:i};this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),e.trigger_config=a}else this._taskId&&(e.trigger_config=null)}else if(this._scheduleType==="sensor_based"&&this._triggerEntityId){let i=this._triggerEntityIds.length>0?this._triggerEntityIds:[this._triggerEntityId],a={entity_id:i[0],entity_ids:i,type:this._triggerType};if(dn(a,this._typeFields()),this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),i.length>1&&(a.entity_logic=this._triggerEntityLogic),this._triggerType==="counter"&&this._triggerDeltaMode&&this._triggerBaselineValue){let o=parseFloat(this._triggerBaselineValue);!isNaN(o)&&o>=0&&(a.trigger_baseline_value=o)}else if(this._triggerType==="runtime"&&this._triggerRuntimeMaxSession){let o=parseInt(this._triggerRuntimeMaxSession,10);!isNaN(o)&&o>0&&(a.trigger_runtime_max_session_seconds=o)}e.trigger_config=a}else this._taskId&&(e.trigger_config=null);if(this.scheduleTimeEnabled&&nn.includes(this._scheduleType)){let i=this._scheduleTimeOn?this._scheduleTime.trim():"";e.schedule_time=/^([01]\d|2[0-3]):[0-5]\d$/.test(i)?i:null}if(this.checklistsEnabled){let i=this._checklistText.split(` +`).map(a=>a.trim()).filter(Boolean).slice(0,100);e.checklist=i.length?i:null}if(this.completionActionsEnabled||this._actionPresent){let i=this._actionService.trim();if(i&&/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(i)){let a={service:i},o=this._actionTargetEntity.trim();this._actionTargetStored&&o===this._actionTargetInitial?a.target=JSON.parse(JSON.stringify(this._actionTargetStored)):o&&(a.target={entity_id:o});let d=this._buildActionData();Object.keys(d).length>0&&(a.data=d),this._actionSkipAuto&&(a.skip_auto=!0),e.on_complete_action=a}else e.on_complete_action=null}if(this.completionActionsEnabled){let i={};this._qcNotes.trim()&&(i.notes=this._qcNotes.trim());let a=parseFloat(this._qcCost);isNaN(a)||(i.cost=a);let o=et(this._qcDuration);o!==null&&(i.duration=o),this._qcFeedback&&(i.feedback=this._qcFeedback),e.quick_complete_defaults=Object.keys(i).length?i:null}return e}_close(){this._open=!1,this._warning="",this._pickerProbeTimer!==void 0&&(clearTimeout(this._pickerProbeTimer),this._pickerProbeTimer=void 0),this._pickerProbeStrikes=0}_renderTriggerFields(){if(this._scheduleType!=="sensor_based")return h;let e=this._lang,t=this._triggerType==="compound";return l`

${n("trigger_configuration",e)}

@@ -1914,7 +1914,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" .value=${this._triggerType} @change=${i=>this._triggerType=i.target.value} > - ${Ba.map(i=>l``)} + ${fo.map(i=>l``)}
${t?this._renderCompoundEditor():l` @@ -1928,7 +1928,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" 0?this._triggerEntityIds:this._triggerEntityId?[this._triggerEntityId]:[]}} .computeLabel=${()=>n("entity_id",e)} @value-changed=${i=>{let a=(i.detail.value.trigger_entities||[]).filter(Boolean);this._triggerEntityId=a[0]||"",this._triggerEntityIds=a,a[0]?this._fetchEntityAttributes(a[0]):this._fetchEntityAttributes("")}} @@ -1976,7 +1976,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
`:h} - `}_patchCondition(e,t){this._compoundConditions=this._compoundConditions.map((i,a)=>a===e?{...i,...t}:i)}_addCondition(){this._compoundConditions=[...this._compoundConditions,Os()]}_removeCondition(e){this._compoundConditions=this._compoundConditions.filter((t,i)=>i!==e)}_renderCompoundEditor(){let e=this._lang;return l` + `}_patchCondition(e,t){this._compoundConditions=this._compoundConditions.map((i,a)=>a===e?{...i,...t}:i)}_addCondition(){this._compoundConditions=[...this._compoundConditions,on()]}_removeCondition(e){this._compoundConditions=this._compoundConditions.filter((t,i)=>i!==e)}_renderCompoundEditor(){let e=this._lang;return l`
${this._renderConditionTypeFields(e,t)} @@ -2090,8 +2090,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" this._adaptiveAlpha=t.target.value} @@ -2164,7 +2164,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" ${this._renderOnStatesField({value:e.onStates,entityId:a,onInput:o=>this._patchCondition(t,{onStates:o})})} `}return e.type==="due_date"?l` this._patchCondition(t,{daysBefore:a.target.value})}> `:h}_renderUnitSelect(){let e=this._lang;return l`
@@ -2175,12 +2175,12 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" > ${["days","weeks","months","years"].map(t=>l``)} -
`}_toggleWeekday(e){this._weekdays=this._weekdays.includes(e)?this._weekdays.filter(t=>t!==e):[...this._weekdays,e]}_previewScheduleDict(){if(this._scheduleType==="one_time")return this._dueDate?{kind:"one_time",due_date:this._dueDate}:null;if(this._scheduleType==="calendar"&&!this._calendarEntity.trim())return null;if(Jt.includes(this._scheduleType))return{...this._buildSchedule(),...this._recurrenceExtras()};let e=parseInt(this._intervalDays,10);return this._scheduleType==="manual"||!e||e<=0?null:{kind:"interval",every:e,unit:this._intervalUnit,anchor:this._intervalAnchor,...this._recurrenceExtras()}}updated(e){super.updated?.(e),this._scheduleEntityPickerProbe();for(let t of e.keys())if(g._PREVIEW_RELEVANT.has(String(t))){this._schedulePreviewRefresh();return}}_scheduleEntityPickerProbe(){this._entityPickerFallback||this._pickerProbeTimer!==void 0||!this._open||this._scheduleType!=="sensor_based"||(this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),1500))}_probeEntityPickers(){if(this._pickerProbeTimer=void 0,this._entityPickerFallback||!this._open)return;let e=this.shadowRoot?.querySelector("ha-form.entity-picker-form"),t=(this.shadowRoot?.querySelector(".content")?.offsetHeight??0)>0;if(!e||!t){this._pickerProbeStrikes=0;return}let i=(u,_,m=0)=>{if(!(!u||m>10)){(u.tagName?.toLowerCase()??"")==="ha-entity-picker"&&_.push(u);for(let f of[u.shadowRoot,u])if(f)for(let b of Array.from(f.children??[]))i(b,_,m+1)}},a=[...this.shadowRoot?.querySelectorAll("ha-form.entity-picker-form")??[]],o=[];for(let u of a)i(u,o);let d=o.length===0||o.some(u=>u.offsetHeight===0);if(e.offsetHeight===0||d){if(this._pickerProbeStrikes+=1,this._pickerProbeStrikes>=2){this._entityPickerFallback=!0;return}this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),700)}else this._pickerProbeStrikes=0}_schedulePreviewRefresh(){this._previewTimer&&clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._fetchSchedulePreview()},300)}async _fetchSchedulePreview(){let e=this._open?this._previewScheduleDict():null;if(!e){this._schedulePreview=[],this._schedulePreviewEnded=!1;return}let t=++this._previewSeq;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/schedule/preview",schedule:e,...this._lastPerformed?{last_performed:this._lastPerformed}:{}});if(t!==this._previewSeq)return;this._schedulePreview=i.occurrences||[],this._schedulePreviewEnded=!!i.series_ended}catch{}}_renderSchedulePreview(){if(this._schedulePreview.length===0)return h;let e=this._lang,t=this.scheduleTimeEnabled&&this._scheduleTimeOn&&this._scheduleTime?` ${this._scheduleTime}`:"",i=this._schedulePreview.map((o,d)=>{let u=new Date(`${o}T12:00:00`).getDay();return`${Fe(u===0?6:u-1,e,"short")} ${F(o,e)}${d===0?t:""}`}).join(" \xB7 "),a=this._scheduleType==="time_based"&&this._intervalAnchor==="completion"?l`
${n("schedule_preview_ontime",e)}
`:h;return l` + `}_toggleWeekday(e){this._weekdays=this._weekdays.includes(e)?this._weekdays.filter(t=>t!==e):[...this._weekdays,e]}_previewScheduleDict(){if(this._scheduleType==="one_time")return this._dueDate?{kind:"one_time",due_date:this._dueDate}:null;if(this._scheduleType==="calendar"&&!this._calendarEntity.trim())return null;if(ni.includes(this._scheduleType))return{...this._buildSchedule(),...this._recurrenceExtras()};let e=parseInt(this._intervalDays,10);return this._scheduleType==="manual"||!e||e<=0?null:{kind:"interval",every:e,unit:this._intervalUnit,anchor:this._intervalAnchor,...this._recurrenceExtras()}}updated(e){super.updated?.(e),this._scheduleEntityPickerProbe();for(let t of e.keys())if(g._PREVIEW_RELEVANT.has(String(t))){this._schedulePreviewRefresh();return}}_scheduleEntityPickerProbe(){this._entityPickerFallback||this._pickerProbeTimer!==void 0||!this._open||this._scheduleType!=="sensor_based"||(this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),1500))}_probeEntityPickers(){if(this._pickerProbeTimer=void 0,this._entityPickerFallback||!this._open)return;let e=this.shadowRoot?.querySelector("ha-form.entity-picker-form"),t=(this.shadowRoot?.querySelector(".content")?.offsetHeight??0)>0;if(!e||!t){this._pickerProbeStrikes=0;return}let i=(p,_,m=0)=>{if(!(!p||m>10)){(p.tagName?.toLowerCase()??"")==="ha-entity-picker"&&_.push(p);for(let f of[p.shadowRoot,p])if(f)for(let x of Array.from(f.children??[]))i(x,_,m+1)}},a=[...this.shadowRoot?.querySelectorAll("ha-form.entity-picker-form")??[]],o=[];for(let p of a)i(p,o);let d=o.length===0||o.some(p=>p.offsetHeight===0);if(e.offsetHeight===0||d){if(this._pickerProbeStrikes+=1,this._pickerProbeStrikes>=2){this._entityPickerFallback=!0;return}this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),700)}else this._pickerProbeStrikes=0}_schedulePreviewRefresh(){this._previewTimer&&clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._fetchSchedulePreview()},300)}async _fetchSchedulePreview(){let e=this._open?this._previewScheduleDict():null;if(!e){this._schedulePreview=[],this._schedulePreviewEnded=!1;return}let t=++this._previewSeq;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/schedule/preview",schedule:e,...this._lastPerformed?{last_performed:this._lastPerformed}:{}});if(t!==this._previewSeq)return;this._schedulePreview=i.occurrences||[],this._schedulePreviewEnded=!!i.series_ended}catch{}}_renderSchedulePreview(){if(this._schedulePreview.length===0)return h;let e=this._lang,t=this.scheduleTimeEnabled&&this._scheduleTimeOn&&this._scheduleTime?` ${this._scheduleTime}`:"",i=this._schedulePreview.map((o,d)=>{let p=new Date(`${o}T12:00:00`).getDay();return`${Ke(p===0?6:p-1,e,"short")} ${O(o,e)}${d===0?t:""}`}).join(" \xB7 "),a=this._scheduleType==="time_based"&&this._intervalAnchor==="completion"?l`
${n("schedule_preview_ontime",e)}
`:h;return l`
${n("schedule_preview_title",e)}: ${i}${this._schedulePreviewEnded?l` ${n("schedule_preview_ends",e)}`:h} ${a}
- `}_buildSchedule(){let e=i=>{let a=parseInt(this._calOffset,10)||0;return a&&(i.offset=Math.max(-gt,Math.min(a,gt))),i};if(this._scheduleType==="weekdays")return e({kind:"weekdays",weekdays:[...this._weekdays].sort((i,a)=>i-a)});if(this._scheduleType==="nth_weekday")return e({kind:"nth_weekday",nth:parseInt(this._nth,10),weekday:parseInt(this._nthWeekday,10)});if(this._scheduleType==="calendar")return e({kind:"calendar",entity_id:this._calendarEntity.trim()});let t={kind:"day_of_month",day:this._domLastDay?-1:parseInt(this._domDay,10)||1};return this._domBusiness&&(t.business=!0),e(t)}_recurrenceExtras(){let e={};if(this._seasonMonths.length&&(e.season_months=[...this._seasonMonths].sort((t,i)=>t-i)),this._endsMode==="count"){let t=parseInt(this._endsCount,10);t>=1&&(e.ends={count:t})}else this._endsMode==="until"&&this._endsUntil&&(e.ends={until:this._endsUntil});return e}_toggleSeasonMonth(e){this._seasonMonths=this._seasonMonths.includes(e)?this._seasonMonths.filter(t=>t!==e):[...this._seasonMonths,e]}_renderRecurrenceExtras(){let e=this._lang;if(!(this._scheduleType==="time_based"||Jt.includes(this._scheduleType)))return h;let i=Ya(e);return l` + `}_buildSchedule(){let e=i=>{let a=parseInt(this._calOffset,10)||0;return a&&(i.offset=Math.max(-kt,Math.min(a,kt))),i};if(this._scheduleType==="weekdays")return e({kind:"weekdays",weekdays:[...this._weekdays].sort((i,a)=>i-a)});if(this._scheduleType==="nth_weekday")return e({kind:"nth_weekday",nth:parseInt(this._nth,10),weekday:parseInt(this._nthWeekday,10)});if(this._scheduleType==="calendar")return e({kind:"calendar",entity_id:this._calendarEntity.trim()});let t={kind:"day_of_month",day:this._domLastDay?-1:parseInt(this._domDay,10)||1};return this._domBusiness&&(t.business=!0),e(t)}_recurrenceExtras(){let e={};if(this._seasonMonths.length&&(e.season_months=[...this._seasonMonths].sort((t,i)=>t-i)),this._endsMode==="count"){let t=parseInt(this._endsCount,10);t>=1&&(e.ends={count:t})}else this._endsMode==="until"&&this._endsUntil&&(e.ends={until:this._endsUntil});return e}_toggleSeasonMonth(e){this._seasonMonths=this._seasonMonths.includes(e)?this._seasonMonths.filter(t=>t!==e):[...this._seasonMonths,e]}_renderRecurrenceExtras(){let e=this._lang;if(!(this._scheduleType==="time_based"||ni.includes(this._scheduleType)))return h;let i=wo(e);return l`
${n("season_window_hint",e)}
@@ -2217,7 +2217,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" .value=${this._endsUntil} @value-changed=${a=>this._endsUntil=a.detail.value} >`:h} - `}_renderCalendarFields(){let e=this._lang,t=Ka(e);if(this._scheduleType==="weekdays")return l` + `}_renderCalendarFields(){let e=this._lang,t=xo(e);if(this._scheduleType==="weekdays")return l`
${t.map((i,a)=>l` @@ -2283,11 +2283,11 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" label="${n("recurrence_offset",e)}" helper="${n("recurrence_offset_help",e)}" type="number" - min=${-gt} - max=${gt} + min=${-kt} + max=${kt} .value=${this._calOffset} @input=${t=>this._calOffset=t.target.value} - >`}_thresholdLimitsOverlap(){let e=parseFloat(this._triggerAbove),t=parseFloat(this._triggerBelow);return!isNaN(e)&&!isNaN(t)&&t>e}_renderTriggerLiveHint(){if(this._triggerType==="compound")return h;let e=this._triggerType==="threshold"&&this._thresholdLimitsOverlap()?l`
${n("trigger_hint_overlap",this._lang)}
`:h,t=this._triggerEntityId||this._triggerEntityIds[0];if(!t||!this.hass?.states)return e;let i=this.hass.states[t];if(!i)return e;let a=this._lang,o=i.attributes?.unit_of_measurement,d=typeof o=="string"&&o?` ${o}`:"",u=this._triggerAttribute?i.attributes?.[this._triggerAttribute]:i.state,_=typeof u=="number"?u:parseFloat(String(u)),m=u!=="unknown"&&u!=="unavailable"&&u!=null&&!isNaN(_),f=w=>I(w,a,{maximumFractionDigits:1}),b=[];if(this._triggerType==="threshold"){let w=parseFloat(this._triggerAbove),k=parseFloat(this._triggerBelow);if(isNaN(w)&&isNaN(k))return h;m&&b.push(n("trigger_hint_now",a).replace("{value}",f(_)+d)),isNaN(w)||b.push(n("trigger_hint_above",a).replace("{target}",f(w)+d)),isNaN(k)||b.push(n("trigger_hint_below",a).replace("{target}",f(k)+d))}else if(this._triggerType==="counter"){let w=parseFloat(this._triggerTargetValue);if(isNaN(w))return h;this._triggerDeltaMode?this._taskId?b.push(n("trigger_hint_counter_delta_edit",a).replace("{target}",f(w)+d)):m?b.push(n("trigger_hint_counter_delta",a).replace("{value}",f(_)+d).replace("{due}",f(_+w)+d).replace("{target}",f(w)+d)):b.push(n("trigger_hint_counter_delta_edit",a).replace("{target}",f(w)+d)):(m&&b.push(n("trigger_hint_now",a).replace("{value}",f(_)+d)),b.push(n("trigger_hint_counter_abs",a).replace("{target}",f(w)+d)))}else if(this._triggerType==="runtime"){let w=parseFloat(this._triggerRuntimeHours);if(isNaN(w))return h;b.push(n("trigger_hint_runtime",a).replace("{hours}",f(w))),b.push(n("trigger_hint_state_now",a).replace("{value}",String(i.state)))}else if(this._triggerType==="state_change"){let w=parseInt(this._triggerTargetChanges,10)||1,k=this._triggerToState.trim();b.push((k?n("trigger_hint_state_change_to",a).replace("{state}",k):n("trigger_hint_state_change",a)).replace("{count}",String(w))),b.push(n("trigger_hint_state_now",a).replace("{value}",String(i.state)))}else if(this._triggerType==="due_date"){let w=parseInt(this._triggerDaysBefore,10)||0;b.push(w===0?n("trigger_hint_due_date_same_day",a):w===1?n("trigger_hint_due_date_one",a):n("trigger_hint_due_date",a).replace("{days}",String(w)));let k=u==null?"":String(u);k&&!isNaN(Date.parse(k))&&b.push(n("trigger_hint_due_date_now",a).replace("{date}",F(k,a)))}return b.length?l`
${b.join(" ")}
${e}`:e}_renderTriggerTypeFields(){let e=this._lang;return this._triggerType==="threshold"?l` + >`}_thresholdLimitsOverlap(){let e=parseFloat(this._triggerAbove),t=parseFloat(this._triggerBelow);return!isNaN(e)&&!isNaN(t)&&t>e}_renderTriggerLiveHint(){if(this._triggerType==="compound")return h;let e=this._triggerType==="threshold"&&this._thresholdLimitsOverlap()?l`
${n("trigger_hint_overlap",this._lang)}
`:h,t=this._triggerEntityId||this._triggerEntityIds[0];if(!t||!this.hass?.states)return e;let i=this.hass.states[t];if(!i)return e;let a=this._lang,o=i.attributes?.unit_of_measurement,d=typeof o=="string"&&o?` ${o}`:"",p=this._triggerAttribute?i.attributes?.[this._triggerAttribute]:i.state,_=typeof p=="number"?p:parseFloat(String(p)),m=p!=="unknown"&&p!=="unavailable"&&p!=null&&!isNaN(_),f=w=>C(w,a,{maximumFractionDigits:1}),x=[];if(this._triggerType==="threshold"){let w=parseFloat(this._triggerAbove),k=parseFloat(this._triggerBelow);if(isNaN(w)&&isNaN(k))return h;m&&x.push(n("trigger_hint_now",a).replace("{value}",f(_)+d)),isNaN(w)||x.push(n("trigger_hint_above",a).replace("{target}",f(w)+d)),isNaN(k)||x.push(n("trigger_hint_below",a).replace("{target}",f(k)+d))}else if(this._triggerType==="counter"){let w=parseFloat(this._triggerTargetValue);if(isNaN(w))return h;this._triggerDeltaMode?this._taskId?x.push(n("trigger_hint_counter_delta_edit",a).replace("{target}",f(w)+d)):m?x.push(n("trigger_hint_counter_delta",a).replace("{value}",f(_)+d).replace("{due}",f(_+w)+d).replace("{target}",f(w)+d)):x.push(n("trigger_hint_counter_delta_edit",a).replace("{target}",f(w)+d)):(m&&x.push(n("trigger_hint_now",a).replace("{value}",f(_)+d)),x.push(n("trigger_hint_counter_abs",a).replace("{target}",f(w)+d)))}else if(this._triggerType==="runtime"){let w=parseFloat(this._triggerRuntimeHours);if(isNaN(w))return h;x.push(n("trigger_hint_runtime",a).replace("{hours}",f(w))),x.push(n("trigger_hint_state_now",a).replace("{value}",String(i.state)))}else if(this._triggerType==="state_change"){let w=parseInt(this._triggerTargetChanges,10)||1,k=this._triggerToState.trim();x.push((k?n("trigger_hint_state_change_to",a).replace("{state}",k):n("trigger_hint_state_change",a)).replace("{count}",String(w))),x.push(n("trigger_hint_state_now",a).replace("{value}",String(i.state)))}else if(this._triggerType==="due_date"){let w=parseInt(this._triggerDaysBefore,10)||0;x.push(w===0?n("trigger_hint_due_date_same_day",a):w===1?n("trigger_hint_due_date_one",a):n("trigger_hint_due_date",a).replace("{days}",String(w)));let k=p==null?"":String(p);k&&!isNaN(Date.parse(k))&&x.push(n("trigger_hint_due_date_now",a).replace("{date}",O(k,a)))}return x.length?l`
${x.join(" ")}
${e}`:e}_renderTriggerTypeFields(){let e=this._lang;return this._triggerType==="threshold"?l` this._triggerTargetChanges=t.target.value} > @@ -2369,7 +2369,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" this._triggerForMinutes=t.target.value} > @@ -2397,8 +2397,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" label="${n("days_before",e)}" type="number" step="1" - min=${mt[0]} - max=${mt[1]} + min=${wt[0]} + max=${wt[1]} .value=${this._triggerDaysBefore} @input=${t=>this._triggerDaysBefore=t.target.value} > @@ -2432,7 +2432,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" .value=${this._type} @change=${i=>this._type=i.target.value} > - ${za.map(i=>l``)} + ${_o.map(i=>l``)}
${this._type==="reading"?l` @@ -2467,7 +2467,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" .value=${this._priority} @change=${i=>this._priority=i.target.value} > - ${Fa.map(i=>l``)} + ${mo.map(i=>l``)}
@@ -2505,7 +2505,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" .value=${this._scheduleType} @change=${i=>this._scheduleType=i.target.value} > - ${Ua.map(i=>l``)} + ${go.map(i=>l``)}
${this._scheduleType==="time_based"?l` @@ -2538,7 +2538,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" @value-changed=${i=>this._dueDate=i.detail.value} > `:h} - ${this.scheduleTimeEnabled&&Hs.includes(this._scheduleType)?l` + ${this.scheduleTimeEnabled&&nn.includes(this._scheduleType)?l`