456 files

This commit is contained in:
Home Assistant Version Control
2026-10-01 05:38:21 +00:00
parent 4192ecbaae
commit 5d6d845ebf
456 changed files with 64951 additions and 3828 deletions
@@ -27,7 +27,7 @@ describe("dashboard KPI chips (#86)", () => {
);
// Reactive template, not the literal number.
expect(content).to.include("{{ states('sensor.maintenance_supporter_overdue') }}");
expect(content).to.include("overdue");
expect(content).to.include("Overdue");
});
it("falls back to the literal WS counts when no summary sensor exists", () => {
@@ -40,15 +40,15 @@ describe("dashboard KPI chips (#86)", () => {
// No entity templates at all — the counts are baked in as real numbers.
expect(content).to.not.include("states(");
expect(content).to.not.include("unknown");
expect(content).to.include("**2** overdue");
expect(content).to.include("**1** triggered");
expect(content).to.include("**3** due soon");
expect(content).to.include("**4** ok");
expect(content).to.include("**2** Overdue");
expect(content).to.include("**1** Triggered");
expect(content).to.include("**3** Due Soon");
expect(content).to.include("**4** OK");
});
it("shows an em dash rather than a template when neither id nor count is available", () => {
const content = contentOf(kpiMarkdownCard({}, {}));
expect(content).to.not.include("states(");
expect(content).to.include("**—** overdue");
expect(content).to.include("**—** Overdue");
});
});
@@ -94,7 +94,10 @@ describe("settings-view print QR section", () => {
await el.updateComplete;
const chips = qrSection(el)!.querySelectorAll<HTMLElement>(".qr-action-chip");
expect(chips.length, "three action chips rendered").to.equal(3);
// view / complete / skip + quick-complete (#192), disabled here: no
// task of the mock objects has quick-complete defaults
expect(chips.length, "four action chips rendered").to.equal(4);
expect(chips[3].classList.contains("disabled"), "quick-complete chip disabled without defaults").to.be.true;
// Default: only "view" active
const viewChip = [...chips].find(c => /view|anzeigen/i.test(c.textContent || ""))!;
expect(viewChip.classList.contains("active"), "view chip active by default").to.be.true;
@@ -285,6 +285,6 @@ if (!customElements.get("maintenance-budget-section-card")) {
registerCustomCard({
type: "maintenance-budget-section-card",
name: "Maintenance Supporter — Budget",
description: "Inline monthly + yearly budget editor",
description: "Edit the monthly and yearly budget right on the dashboard.",
preview: false,
});
@@ -323,6 +323,6 @@ if (!customElements.get("maintenance-groups-section-card")) {
registerCustomCard({
type: "maintenance-groups-section-card",
name: "Maintenance Supporter — Groups",
description: "Inline group CRUD",
description: "Create, rename and delete task groups right on the dashboard.",
preview: false,
});
@@ -1,7 +1,9 @@
/** Dialog for generating, printing, and downloading QR codes.
*
* For tasks: shows two QR codes side-by-side — "Info" (ℹ) and "Complete" (✓)
* with embedded icons. For objects: shows a single "Info" QR.
* with embedded icons, plus the lightning-bolt "Quick-complete" code when the
* task has quick-complete defaults (#192: the code existed since 1.3.0 but no
* dialog ever offered it). For objects: shows a single "Info" QR.
*/
import { LitElement, html, css, nothing } from "lit";
@@ -48,6 +50,7 @@ export class MaintenanceQrDialog extends LitElement {
@state() private _error = "";
@state() private _viewResult: QrResult | null = null;
@state() private _completeResult: QrResult | null = null;
@state() private _quickResult: QrResult | null = null;
@state() private _urlMode: "companion" | "local" | "server" = "companion";
private _entryId = "";
@@ -65,6 +68,7 @@ export class MaintenanceQrDialog extends LitElement {
this._error = "";
this._viewResult = null;
this._completeResult = null;
this._quickResult = null;
this._open = true;
this._generate();
}
@@ -83,6 +87,7 @@ export class MaintenanceQrDialog extends LitElement {
this._error = "";
this._viewResult = null;
this._completeResult = null;
this._quickResult = null;
this._open = true;
this._generate();
}
@@ -93,6 +98,7 @@ export class MaintenanceQrDialog extends LitElement {
this._error = "";
this._viewResult = null;
this._completeResult = null;
this._quickResult = null;
try {
const base: Record<string, unknown> = {
type: "maintenance_supporter/qr/generate",
@@ -109,6 +115,13 @@ export class MaintenanceQrDialog extends LitElement {
promises.push(
this.hass.connection.sendMessagePromise({ ...base, action: "complete" }),
);
// The one-tap code only when the task has defaults to record — for
// any other task it would just open the complete dialog.
if (await this._hasQuickCompleteDefaults()) {
promises.push(
this.hass.connection.sendMessagePromise({ ...base, action: "quick_complete" }),
);
}
}
const results = await Promise.all(promises);
@@ -118,6 +131,9 @@ export class MaintenanceQrDialog extends LitElement {
if (results.length > 1) {
this._completeResult = results[1] as QrResult;
}
if (results.length > 2) {
this._quickResult = results[2] as QrResult;
}
} catch (err: unknown) {
if (seq !== this._generateSeq) return;
const code = (err as Record<string, unknown>)?.code;
@@ -130,6 +146,18 @@ export class MaintenanceQrDialog extends LitElement {
}
}
private async _hasQuickCompleteDefaults(): Promise<boolean> {
try {
const obj = await this.hass.connection.sendMessagePromise<{
tasks?: Array<{ id: string; quick_complete_defaults?: Record<string, unknown> | null }>;
}>({ type: "maintenance_supporter/object", entry_id: this._entryId });
const task = (obj.tasks || []).find((tk) => tk.id === this._taskId);
return !!task?.quick_complete_defaults && Object.keys(task.quick_complete_defaults).length > 0;
} catch {
return false;
}
}
private _setUrlMode(mode: "companion" | "local" | "server"): void {
if (this._urlMode === mode) return;
this._urlMode = mode;
@@ -152,8 +180,10 @@ export class MaintenanceQrDialog extends LitElement {
const safeSub = escapeHtml(subtitle);
const hasComplete = !!this._completeResult;
const hasQuick = !!this._quickResult;
const viewLabel = escapeHtml(t("qr_action_view", L));
const completeLabel = escapeHtml(t("qr_action_complete", L));
const quickLabel = escapeHtml(t("qr_action_quick_complete", L));
w.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
<meta name="color-scheme" content="light">
@@ -169,7 +199,7 @@ export class MaintenanceQrDialog extends LitElement {
.sub{color:#666;font-size:14px;margin-bottom:16px}
.qr-row{display:flex;justify-content:center;gap:24px;margin:12px 0}
.qr-col{display:flex;flex-direction:column;align-items:center;gap:6px}
.qr-col img{width:${hasComplete ? "200px" : "280px"}}
.qr-col img{width:${hasQuick ? "170px" : hasComplete ? "200px" : "280px"}}
.qr-label{font-size:13px;font-weight:500;color:#333}
.url{font-size:10px;color:#999;word-break:break-all;margin-top:8px;max-width:480px}
</style></head><body>
@@ -184,6 +214,10 @@ ${safeSub ? `<div class="sub">${safeSub}</div>` : ""}
<img src="${sanitizeDataUri(this._completeResult!.svg_data_uri)}" alt="QR Complete" />
<div class="qr-label">${completeLabel}</div>
</div>` : ""}
${hasQuick ? `<div class="qr-col">
<img src="${sanitizeDataUri(this._quickResult!.svg_data_uri)}" alt="QR Quick-complete" />
<div class="qr-label">${quickLabel}</div>
</div>` : ""}
</div>
<div class="url">${escapeHtml(this._viewResult.url)}</div>
<script>setTimeout(()=>window.print(),300)<\/script>
@@ -208,6 +242,7 @@ ${safeSub ? `<div class="sub">${safeSub}</div>` : ""}
this._open = false;
this._viewResult = null;
this._completeResult = null;
this._quickResult = null;
this._error = "";
this._loading = false;
}
@@ -260,6 +295,23 @@ ${safeSub ? `<div class="sub">${safeSub}</div>` : ""}
</div>
`
: nothing}
${this._quickResult
? html`
<div class="qr-item quick">
<img
class="qr-image small"
src="${this._quickResult.svg_data_uri}"
alt="QR Quick-complete"
/>
<div class="qr-item-label">${t("qr_action_quick_complete", L)}</div>
<button class="dl-btn"
@click=${() => this._downloadSvg(this._quickResult!, "quick-complete")}>
<ha-icon icon="mdi:download"></ha-icon>
${t("qr_download", L)}
</button>
</div>
`
: nothing}
</div>
<div class="url-display">${this._viewResult!.url}</div>
`
@@ -312,6 +364,7 @@ ${safeSub ? `<div class="sub">${safeSub}</div>` : ""}
}
.qr-pair {
display: flex;
flex-wrap: wrap; /* three codes (quick-complete, #192) wrap on a phone */
gap: 20px;
justify-content: center;
width: 100%;
@@ -332,6 +385,7 @@ ${safeSub ? `<div class="sub">${safeSub}</div>` : ""}
height: 180px;
}
.qr-item-label {
max-width: 180px;
font-size: 12px;
font-weight: 500;
color: var(--secondary-text-color);
@@ -55,6 +55,8 @@ interface SettingsResponse {
shopping_list_entity?: string;
panel_enabled: boolean;
panel_title: string;
/** 2.94: the sidebar title an empty field means (server language). */
panel_title_default?: string;
/** Opt-in copy of the shipped Assist sentences into the config dir. */
install_assist_sentences?: boolean;
/** #146: household "low" floors (percent) for discovery + battery fleet. */
@@ -207,7 +209,7 @@ export class MaintenanceSettingsView extends LitElement {
@state() private _vacSaving = false;
// Print QR codes section state
@state() private _qrObjects: Array<{ entry_id: string; name: string; task_count: number }> = [];
@state() private _qrObjects: Array<{ entry_id: string; name: string; task_count: number; quick_count: number }> = [];
@state() private _qrSelectedEntries = new Set<string>();
@state() private _qrActions = new Set<string>(["view"]);
@state() private _qrUrlMode: "server" | "local" | "companion" = "companion";
@@ -220,7 +222,7 @@ export class MaintenanceSettingsView extends LitElement {
@state() private _qrObjectsLoaded = false;
// Export/Import selective-object picker state
@state() private _exportObjects: Array<{ entry_id: string; name: string; task_count: number }> = [];
@state() private _exportObjects: Array<{ entry_id: string; name: string; task_count: number; quick_count: number }> = [];
@state() private _exportSelectedEntries = new Set<string>();
@state() private _exportObjectsLoaded = false;
@state() private _docArchiveLoading = false;
@@ -1063,7 +1065,7 @@ export class MaintenanceSettingsView extends LitElement {
<label class="setting-row">
<span class="setting-label">${t("settings_panel_title", L)}</span>
<input type="text" .value=${g.panel_title ?? ""}
placeholder="Maintenance"
placeholder=${g.panel_title_default || "Maintenance"}
maxlength="50"
@change=${(e: Event) => this._updateSetting("panel_title", (e.target as HTMLInputElement).value.trim())} />
</label>
@@ -1684,10 +1686,22 @@ export class MaintenanceSettingsView extends LitElement {
// --- Section: Print QR codes (v1.1.0) ---
/** The objects the batch prints for: the ticked ones, or all when none is. */
private _qrSelectedRows(): Array<{ entry_id: string; task_count: number; quick_count: number }> {
return this._qrSelectedEntries.size === 0 ? this._qrObjects : this._qrObjects.filter((o) => this._qrSelectedEntries.has(o.entry_id));
}
private _renderPrintQr(L: string) {
const selectedCount = this._qrSelectedEntries.size || this._qrObjects.length;
const rows = this._qrSelectedRows();
const taskCount = rows.reduce((n, o) => n + o.task_count, 0);
// #192: the quick-complete code only exists for tasks with defaults —
// offered when the chosen objects have any (not tied to the completion-
// actions switch: defaults set before it was turned off still work).
const quickCount = rows.reduce((n, o) => n + o.quick_count, 0);
// One code per TASK and action (the estimate multiplied OBJECTS, so the
// 200 warning came too late and the server refused the batch).
const estimatedQrs = [...this._qrActions].reduce((n, a) => n + (a === "quick_complete" ? quickCount : taskCount), 0);
const actionCount = this._qrActions.size;
const estimatedQrs = selectedCount * actionCount;
const overLimit = estimatedQrs > 200;
return html`
@@ -1721,15 +1735,21 @@ export class MaintenanceSettingsView extends LitElement {
<div class="qr-filter-group">
<div class="qr-filter-label">${t("qr_print_actions", L)}</div>
<div class="qr-action-chips">
${["view", "complete", "skip"].map((a) => html`
<label class="qr-action-chip ${this._qrActions.has(a) ? "active" : ""}">
${["view", "complete", "skip", "quick_complete"].map((a) => {
const quickNone = a === "quick_complete" && quickCount === 0;
return html`
<label class="qr-action-chip ${this._qrActions.has(a) && !quickNone ? "active" : ""} ${quickNone ? "disabled" : ""}"
title=${quickNone ? t("qr_print_quick_none", L) : nothing}>
<input type="checkbox"
.checked=${this._qrActions.has(a)}
.checked=${this._qrActions.has(a) && !quickNone}
.disabled=${quickNone}
@change=${(e: Event) => this._toggleQrAction(a, (e.target as HTMLInputElement).checked)} />
${t("qr_action_" + a, L)}
${t("qr_action_" + a, L)}${a === "quick_complete" && quickCount > 0 ? ` (${quickCount})` : ""}
</label>
`)}
`;
})}
</div>
${quickCount === 0 ? html`<div class="hint qr-quick-hint">${t("qr_print_quick_none", L)}</div>` : nothing}
</div>
<div class="qr-filter-group">
@@ -1787,10 +1807,14 @@ export class MaintenanceSettingsView extends LitElement {
this._qrObjectsLoaded = true;
}
/** {entry_id, name, task_count} of every object, name-sorted — the QR
* batch and the export section both pick objects from this list. */
private async _loadObjectRows(): Promise<Array<{ entry_id: string; name: string; task_count: number }> | undefined> {
const result = await this._ws<{ objects: Array<{ entry_id: string; object: { name: string }; tasks: unknown[] }> }>({
/** {entry_id, name, task_count, quick_count} of every object, name-sorted —
* the QR batch and the export section both pick objects from this list.
* ``quick_count``: tasks with quick-complete defaults (#192), the only
* ones a quick-complete code is printed for. */
private async _loadObjectRows(): Promise<Array<{ entry_id: string; name: string; task_count: number; quick_count: number }> | undefined> {
const result = await this._ws<{
objects: Array<{ entry_id: string; object: { name: string }; tasks: Array<{ quick_complete_defaults?: Record<string, unknown> | null }> }>;
}>({
type: "maintenance_supporter/objects",
});
if (!result) return undefined;
@@ -1798,6 +1822,7 @@ export class MaintenanceSettingsView extends LitElement {
entry_id: o.entry_id,
name: o.object.name,
task_count: (o.tasks || []).length,
quick_count: (o.tasks || []).filter((tk) => tk.quick_complete_defaults && Object.keys(tk.quick_complete_defaults).length > 0).length,
})).sort((a, b) => a.name.localeCompare(b.name));
}
@@ -2596,6 +2621,10 @@ export class MaintenanceSettingsView extends LitElement {
cursor: pointer;
user-select: none;
}
.qr-action-chip.disabled {
opacity: 0.5;
cursor: default;
}
.qr-action-chip.active {
background: var(--primary-color, #03a9f4);
color: #fff;
@@ -14,6 +14,11 @@
* under another name starts unticked with that name, tasks it has by name are
* listed as already there, and a before/after line says what adopting does.
* Picking another target asks the server again (integration_setups/preview).
*
* 2.95: a duty whose integration counts the consumable itself names the
* reset button completing it will press; tasks adopted before (or wired by
* hand) are offered the same in their own section, with a before/after line
* (integration_setups/reset_offers + wire_resets).
*/
import { css, html, LitElement, nothing } from "lit";
@@ -30,8 +35,25 @@ interface SetupTask {
threshold: number;
direction: string;
covered_by?: { task_id: string; name: string; reason: string } | null;
/** 2.95: the integration's reset button completing this task presses. */
reset?: { entity_id: string; name: string; disabled: boolean } | null;
}
interface ResetOffer {
entry_id: string;
object_name: string;
task_id: string;
task_name: string;
integration_name: string;
button_entity_id: string;
button_name: string;
button_disabled: boolean;
/** Adopted before the fingerprint and renamed since — offered unticked. */
renamed?: boolean;
}
const offerKey = (o: ResetOffer): string => `${o.entry_id}/${o.task_id}`;
interface SuggestedSetup {
device_id: string;
device_name: string;
@@ -74,6 +96,10 @@ export class MaintenanceSuggestedSetupsDialog extends LitElement {
// Ticked tasks per device (task_name keys).
@state() private _tasks: Map<string, Set<string>> = new Map();
@state() private _objects: Array<{ entry_id: string; name: string }> = [];
// 2.95: existing tasks that could also reset the integration's counter.
@state() private _offers: ResetOffer[] = [];
@state() private _offerTicks: Set<string> = new Set();
@state() private _wiring = false;
private _localeReady = false;
@@ -99,6 +125,8 @@ export class MaintenanceSuggestedSetupsDialog extends LitElement {
this._error = "";
this._setups = [];
this._selected = new Set();
this._offers = [];
void this._loadOffers();
const resp = await runWs<{ setups: SuggestedSetup[] }>(
this,
{ type: "maintenance_supporter/integration_setups/discover" },
@@ -129,6 +157,70 @@ export class MaintenanceSuggestedSetupsDialog extends LitElement {
this._open = false;
}
private async _loadOffers(): Promise<void> {
try {
const r = await this.hass.connection.sendMessagePromise<{ offers: ResetOffer[] }>({
type: "maintenance_supporter/integration_setups/reset_offers",
});
this._offers = r.offers || [];
this._offerTicks = new Set(this._offers.filter((o) => !o.renamed).map(offerKey));
} catch {
this._offers = []; // an older backend has no offers
}
}
private _toggleOffer(o: ResetOffer): void {
const next = new Set(this._offerTicks);
const key = offerKey(o);
if (next.has(key)) next.delete(key);
else next.add(key);
this._offerTicks = next;
}
private _wire = async (): Promise<void> => {
if (this._wiring) return;
const items = this._offers.filter((o) => this._offerTicks.has(offerKey(o))).map((o) => ({ entry_id: o.entry_id, task_id: o.task_id }));
if (items.length === 0) return;
this._error = "";
const result = await runWs<{ wired: number }>(
this,
{ type: "maintenance_supporter/integration_setups/wire_resets", items },
{ busy: (b) => { this._wiring = b; }, onError: (m) => { this._error = m; } },
);
if (result === undefined) return;
this.dispatchEvent(new CustomEvent("reset-counters-wired", { bubbles: true, composed: true, detail: result }));
await this._loadOffers();
};
private _renderOffers(L: string) {
if (this._offers.length === 0) return nothing;
const ticked = this._offers.filter((o) => this._offerTicks.has(offerKey(o))).length;
return html`
<div class="offers">
<div class="offers-title">${t("reset_offers_title", L)}</div>
<div class="hint">${t("reset_offers_hint", L)}</div>
${this._offers.map(
(o) => html`
<label class="offer" data-task=${o.task_id}>
<input type="checkbox" class="offer-check" .checked=${this._offerTicks.has(offerKey(o))} @change=${() => this._toggleOffer(o)} />
<span class="task-text">
<span>${o.task_name} · ${o.object_name}</span>
${o.renamed ? html`<span class="offer-renamed">${t("reset_offers_renamed", L)}</span>` : nothing}
<span class="offer-before">${t("reset_offers_before", L).replace("{integration}", o.integration_name)}</span>
<span class="offer-after">
${t("reset_offers_after", L).replace("{button}", o.button_name)}${o.button_disabled ? ` ${t("setups_reset_enable", L)}` : ""}
</span>
</span>
</label>
`,
)}
<div class="offers-actions">
<ha-button @click=${this._wire} .disabled=${ticked === 0 || this._wiring}>${t("reset_offers_apply", L)}</ha-button>
</div>
</div>
`;
}
private _toggle = (deviceId: string): void => {
const next = new Set(this._selected);
if (next.has(deviceId)) next.delete(deviceId);
@@ -251,10 +343,11 @@ export class MaintenanceSuggestedSetupsDialog extends LitElement {
<div class="hint">${t("setups_hint", L)}</div>
${this._error ? html`<div class="error">${this._error}</div>` : nothing}
${this._renderOffers(L)}
${this._loading
? html`<div class="loading">…</div>`
: this._setups.length === 0
? html`<div class="empty">${t("setups_none", L)}</div>`
? this._offers.length === 0 ? html`<div class="empty">${t("setups_none", L)}</div>` : nothing
: html`
<div class="list">
${this._setups.map((s) => {
@@ -292,6 +385,9 @@ export class MaintenanceSuggestedSetupsDialog extends LitElement {
${task.covered_by
? html`<span class="maybe">${t("setups_maybe_covered", L).replace("{name}", task.covered_by.name)}</span>`
: nothing}
${task.reset
? html`<span class="reset">↻ ${t("setups_reset_line", L).replace("{button}", task.reset.name)}${task.reset.disabled ? ` ${t("setups_reset_enable", L)}` : ""}</span>`
: nothing}
</span>
</label>
${task.direction === "usage_delta" && ticks.has(task.task_name)
@@ -397,6 +493,18 @@ export class MaintenanceSuggestedSetupsDialog extends LitElement {
.task-text { display: flex; flex-direction: column; min-width: 0; }
.maybe { font-size: 11px; color: var(--warning-color, #ff9800); }
.already { font-size: 11px; color: var(--secondary-text-color); margin-top: 2px; }
.reset { font-size: 11px; color: var(--secondary-text-color); }
.offers {
display: flex; flex-direction: column; gap: 6px; padding: 8px;
border: 1px solid var(--primary-color); border-radius: 6px;
overflow-y: auto; max-height: 40vh; flex-shrink: 0;
}
.offers-title { font-weight: 500; font-size: 14px; }
.offer { display: flex; align-items: flex-start; gap: 6px; font-size: 13px; cursor: pointer; }
.offer-before { font-size: 11px; color: var(--secondary-text-color); }
.offer-after { font-size: 11px; color: var(--primary-color); }
.offer-renamed { font-size: 11px; color: var(--warning-color, #ff9800); }
.offers-actions { display: flex; justify-content: flex-end; }
.before-after {
font-size: 12px; font-weight: 500; color: var(--primary-color);
border-top: 1px dashed var(--divider-color); padding-top: 4px; margin-top: 2px;
@@ -366,6 +366,12 @@ export class MaintenanceTaskDialog extends LitElement {
// so ha-form can drive the data fields when the service schema is known.
@state() private _actionService = "";
@state() private _actionTargetEntity = "";
/** 2.95: not when the task completes itself (a reset wired by discovery). */
@state() private _actionSkipAuto = false;
/** 2.95: the task already has an action (suggested setups wire counter
* resets without the feature toggle) — its section shows regardless, so
* what runs on completion stays visible and removable. */
@state() private _actionPresent = false;
@state() private _actionData: Record<string, unknown> = {};
@state() private _actionDataJsonFallback = "";
@state() private _actionTesting = false;
@@ -540,11 +546,15 @@ export class MaintenanceTaskDialog extends LitElement {
this._actionTargetEntity = Array.isArray(tgt) ? (tgt[0] || "") : (tgt || "");
this._actionData = (oca.data && typeof oca.data === "object") ? { ...oca.data } : {};
this._actionDataJsonFallback = "";
this._actionSkipAuto = oca.skip_auto === true;
this._actionPresent = true;
} else {
this._actionService = "";
this._actionTargetEntity = "";
this._actionData = {};
this._actionDataJsonFallback = "";
this._actionSkipAuto = false;
this._actionPresent = false;
}
const qcd = task.quick_complete_defaults;
this._qcNotes = qcd?.notes || "";
@@ -663,6 +673,8 @@ export class MaintenanceTaskDialog extends LitElement {
this._actionTargetEntity = "";
this._actionData = {};
this._actionDataJsonFallback = "";
this._actionSkipAuto = false;
this._actionPresent = false;
this._actionTesting = false;
this._actionTestResult = "";
this._qcNotes = "";
@@ -857,7 +869,7 @@ export class MaintenanceTaskDialog extends LitElement {
}
private _renderCompletionActionsSection(L: string) {
if (!this.completionActionsEnabled) return nothing;
if (!this.completionActionsEnabled && !this._actionPresent) return nothing;
const schema = this._serviceSchema();
return html`
<details class="ca-section">
@@ -894,6 +906,11 @@ export class MaintenanceTaskDialog extends LitElement {
<p class="field-help ca-domain-hint">
${t("on_complete_action_target_hint", L)}
</p>
<label class="ca-skip-auto">
<input type="checkbox" .checked=${this._actionSkipAuto}
@change=${(e: Event) => { this._actionSkipAuto = (e.target as HTMLInputElement).checked; }} />
<span>${t("on_complete_action_skip_auto", L)}</span>
</label>
${schema
? html`
<ha-form
@@ -933,7 +950,7 @@ export class MaintenanceTaskDialog extends LitElement {
</div>
</details>
<details class="ca-section">
${this.completionActionsEnabled ? html`<details class="ca-section">
<summary>${t("quick_complete_defaults_title", L)}</summary>
<p class="field-help">${t("quick_complete_defaults_desc", L)}</p>
<ms-textfield
@@ -960,7 +977,7 @@ export class MaintenanceTaskDialog extends LitElement {
<option value="needed">${t("quick_complete_defaults_feedback_needed", L)}</option>
<option value="not_needed">${t("quick_complete_defaults_feedback_not_needed", L)}</option>
</select>
</details>
</details>` : nothing}
`;
}
@@ -1668,8 +1685,9 @@ export class MaintenanceTaskDialog extends LitElement {
data.checklist = items.length ? items : null;
}
// v1.3.0: on_complete_action + quick_complete_defaults (gated)
if (this.completionActionsEnabled) {
// v1.3.0: on_complete_action + quick_complete_defaults (gated; the
// action also when the task already had one — 2.95)
if (this.completionActionsEnabled || this._actionPresent) {
const svc = this._actionService.trim();
if (svc && /^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(svc)) {
const action: Record<string, unknown> = { service: svc };
@@ -1679,11 +1697,13 @@ export class MaintenanceTaskDialog extends LitElement {
if (Object.keys(dataDict).length > 0) {
action.data = dataDict;
}
if (this._actionSkipAuto) action.skip_auto = true;
data.on_complete_action = action;
} else {
data.on_complete_action = null;
}
}
if (this.completionActionsEnabled) {
const qcd: Record<string, unknown> = {};
if (this._qcNotes.trim()) qcd.notes = this._qcNotes.trim();
const cost = parseFloat(this._qcCost);
@@ -333,6 +333,6 @@ if (!customElements.get("maintenance-vacation-section-card")) {
registerCustomCard({
type: "maintenance-vacation-section-card",
name: "Maintenance Supporter — Vacation",
description: "Inline vacation mode toggle + dates",
description: "Switch vacation mode on or off and set its dates right on the dashboard.",
preview: false,
});
@@ -6,6 +6,7 @@
*/
import type { MaintenanceObject, MaintenanceTask } from "../types";
import { escapeHtml as esc } from "./html-escape";
export interface ReportLabels {
title: string;
@@ -35,11 +36,6 @@ export interface ReportLabels {
none: string;
}
function esc(v: unknown): string {
return String(v ?? "").replace(/[&<>"']/g, (c) =>
({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c] as string));
}
export function buildObjectReportHtml(
obj: MaintenanceObject,
tasks: MaintenanceTask[],
@@ -32,6 +32,8 @@ export const LS_KEYS = {
objectsCache: "msp-objects-cache",
gettingStartedDismissed: "msp-gs-dismissed",
batteryRosterSort: "ms_bf_roster_sort",
/** #191: the areas table's sort column + direction ("cost_year:desc"). */
areaSort: "msp-area-sort",
} as const;
/**
@@ -241,6 +241,26 @@
"sort_task_name": "Název úkolu",
"all_objects": "Všechny objekty",
"all_parts": "Všechny díly",
"all_areas": "Všechny oblasti",
"areas_filter": "Filtrovat oblasti…",
"areas_filter_none": "Filtru neodpovídá žádná oblast.",
"area_cost_year": "Náklady {year}",
"area_object_count": "Objekty: {n}",
"area_object_count_one": "1 objekt",
"area_range_12m": "Posledních 12 měsíců",
"area_range_all": "Celé období",
"area_task_filter": "Filtrovat podle úkolu…",
"area_kpi_completions": "Dokončení",
"area_kpi_total_time": "Celkový čas",
"area_cost_per_month": "Náklady podle měsíců",
"area_cost_per_year": "Náklady podle let",
"area_cost_per_object": "Náklady podle objektů",
"area_cost_share": "Podíl",
"area_history_section": "Historie (všechny objekty)",
"area_report_print": "Přehled oblasti (PDF)",
"area_report_title": "Přehled oblasti",
"area_report_period": "Období",
"area_no_costs": "V tomto období nejsou zaznamenány žádné náklady.",
"no_tasks_yet": "Zatím žádné úkoly",
"add_first_task": "Přidat první úkol",
"trigger_configuration": "Konfigurace spouštěče",
@@ -399,11 +419,70 @@
"qr_download": "Stáhnout SVG",
"qr_action_view": "Zobrazit informace o údržbě",
"qr_action_complete": "Označit údržbu jako dokončenou",
"qr_action_quick_complete": "Rychlé dokončení — bez dialogu",
"qr_print_quick_none": "Kódy rychlého dokončení vyžadují u úkolu výchozí hodnoty rychlého dokončení (dialog úkolu → Výchozí hodnoty rychlého dokončení) — žádný úkol vybraných objektů je zatím nemá.",
"qr_url_mode": "Typ odkazu",
"qr_mode_companion": "Companion App",
"qr_mode_local": "Lokální (mDNS)",
"qr_mode_server": "URL serveru",
"overview": "Přehled",
"strat_this_month": "Tento měsíc",
"strat_later": "Později",
"strat_week": "Týden",
"strat_fortnight": "Dva týdny",
"strat_month": "Měsíc",
"strat_year": "Rok",
"strat_other": "Ostatní",
"strat_status": "Stav",
"strat_empty_title": "Zatím žádné objekty údržby",
"strat_empty_content": "Otevřete panel Údržba a přidejte první objekt — bazénové čerpadlo, filtr vzduchotechniky, vozidlo, cokoli, co potřebuje pravidelnou péči.",
"strat_open_panel": "Otevřít panel Údržba",
"strat_add_object": "Přidat objekt",
"strat_not_loaded": "**Maintenance Supporter** není načten. Nejprve nainstalujte/povolte integraci.",
"strat_group_by": "Seskupit zobrazení podle",
"strat_group_area": "Podle oblasti (výchozí)",
"strat_group_status": "Podle stavu (Po termínu / Spuštěno / Brzy / OK)",
"strat_group_floor": "Podle podlaží (používá podlaží z HA)",
"strat_group_due_date": "Podle termínu (Po termínu / Dnes / Tento týden / Tento měsíc / Později)",
"strat_group_calendar": "Klouzavý kalendář (Týden / Dva týdny / Měsíc / Rok)",
"strat_editor_help": "Zobrazení „Přehled“ je vždy první. Prázdné skupiny se vynechávají.",
"picker_name_calendar": "Maintenance Supporter — Kalendář",
"picker_name_panel": "Maintenance Supporter — Panel jako karta",
"picker_name_budget": "Maintenance Supporter — Rozpočet údržby",
"picker_name_groups": "Maintenance Supporter — Skupiny",
"picker_name_vacation": "Maintenance Supporter — Režim dovolené",
"picker_name_battery_fleet": "Flotila baterií",
"picker_name_section": "Maintenance Supporter — Sekce",
"picker_name_vacation_status": "Maintenance Supporter — Stav dovolené",
"picker_name_budget_status": "Maintenance Supporter — Stav rozpočtu",
"picker_desc_card": "Přehled úkolů údržby s rychlými akcemi.",
"picker_desc_calendar": "Klouzavý kalendář úkolů údržby s okny 7/14/30/365 dní, ikonami zdroje a štítky přesnosti předpovědi.",
"picker_desc_panel": "Celý panel Maintenance Supporter jako karta — pro zobrazení typu panel / podzobrazení dashboardu bez položky v postranním panelu.",
"picker_desc_budget": "Upravte měsíční a roční rozpočet přímo na dashboardu.",
"picker_desc_groups": "Vytvářejte, přejmenovávejte a mažte skupiny úkolů přímo na dashboardu.",
"picker_desc_vacation": "Zapínejte a vypínejte režim dovolené a nastavte jeho termín přímo na dashboardu.",
"picker_desc_battery_fleet": "Všechny sledované baterie: co je teď slabé, co se brzy vybije a co koupit.",
"picker_desc_dashboard": "Automaticky generovaný dashboard. Seskupte zobrazení podle oblasti, stavu, podlaží nebo termínu — volba v editoru strategie nebo v YAML.",
"picker_desc_section": "Vložte úkoly údržby (filtrovatelné podle oblasti, stavu, termínu) jako sekci do libovolného zobrazení dashboardu.",
"picker_desc_vacation_status": "Kompaktní banner se stavem režimu dovolené. Klepnutím otevřete nastavení v panelu.",
"picker_desc_budget_status": "Přehled měsíčního a ročního rozpočtu na údržbu. Klepnutím zobrazíte podrobnosti.",
"picker_desc_groups_status": "Seznam nastavených skupin úkolů s počtem úkolů v každé.",
"hist_note_button": "Dokončeno tlačítkem na dashboardu",
"hist_note_todo": "Dokončeno ze seznamu úkolů",
"hist_note_voice": "Dokončeno hlasem",
"hist_note_notification": "Dokončeno z oznámení",
"hist_note_nfc": "Dokončeno přes NFC tag",
"hist_note_shopping_list": "Dokončeno z nákupního seznamu",
"hist_note_todo_mirror": "Dokončeno ze zrcadleného seznamu úkolů",
"hist_note_skipped_notification": "Přeskočeno z oznámení",
"hist_note_skipped_button": "Přeskočeno tlačítkem na dashboardu",
"hist_note_sensor_triggered": "Spouštěč senzoru aktivován",
"hist_note_initial": "Počáteční hodnota nastavena při vytvoření úkolu",
"hist_note_auto_recovered": "Automaticky dokončeno: senzor se zotavil ({value})",
"hist_note_reset": "Resetováno na {date}",
"hist_note_trigger_replaced": "Entita spouštěče nahrazena: {old} → {new}",
"hist_note_trigger_removed": "Spouštěč senzoru odebrán (entita: {entity}). Plán převeden na {schedule}.",
"hist_note_compound_removed": "Entita {entity} odebrána ze složeného spouštěče; zbývající podmínky: {count}.",
"recent_activities": "Nedávné aktivity",
"search_notes": "Hledat v poznámkách",
"avg_cost": "Prům. náklady",
@@ -716,6 +795,7 @@
"groups_manage_tasks": "Spravovat přiřazení úkolů…",
"groups_open_panel": "Otevřít v panelu",
"on_complete_action_target_hint": "Poznámka: doména entity musí odpovídat službě — např. 'button.press' funguje jen na button.*, 'counter.increment' jen na counter.*, 'input_button.press' jen na input_button.* atd. Při neshodě akce tiše selže (HA zaznamená 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Ne, když se úkol dokončí sám (jeho senzor se zotavil)",
"show_all_objects": "Zobrazit všechny objekty",
"show_all_tasks": "Vymazat filtr — zobrazit všechny úkoly",
"filter_to_overdue": "Filtrovat seznam jen na po termínu",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "„{name}“: úkoly {before} → {after}",
"adopt_problem_adopt": "Převzít vybrané",
"adopt_problem_done": "Převzato {tasks} senzorů problémů",
"adopt_problem_done_one": "Převzat 1 senzor problémů",
"views_label": "Zobrazení",
"views_none": "— Žádné zobrazení —",
"views_manage": "Uložit / spravovat zobrazení",
@@ -1013,6 +1094,7 @@
"setups_none": "Nenalezena žádná podporovaná zařízení s nepropojenými senzory spotřebního materiálu.",
"setups_adopt": "Nastavit vybrané",
"setups_done": "Vytvořeno {tasks} úkolů propojených se senzory.",
"setups_done_one": "Vytvořen 1 úkol propojený se senzory.",
"complete_parts_used": "Tentokrát použité díly",
"part_delete_confirm": "Smazat díl '{name}'? Odstraní se sledování zásob, vazby na úkoly i případná otevřená připomínka nákupu.",
"baseline_start_value": "Počáteční hodnota (volitelné)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "stejný model na jiném zařízení",
"setups_maybe_covered": "Pravděpodobně už existuje jako „{name}“",
"setups_already": "Už existuje: {names}",
"setups_reset_line": "Dokončení také resetuje čítač v integraci („{button}“)",
"setups_reset_enable": "— tlačítko se k tomu zapne",
"reset_offers_title": "Resetovat čítače při dokončení",
"reset_offers_hint": "Tyto úkoly sledují čítač, který si integrace vede sama. Po propojení dokončení úkolu zde stiskne také resetovací tlačítko integrace — jinak čítač běží dál a úkol je hned znovu splatný.",
"reset_offers_before": "Nyní: dokončení nechá čítač v {integration} běžet dál",
"reset_offers_after": "Poté: stiskne také „{button}“",
"reset_offers_renamed": "Od nastavení přejmenováno — ověřte, že úkol patří k tomuto dílu",
"reset_offers_apply": "Propojit resety",
"reset_offers_done": "Úkoly, které nyní při dokončení resetují čítač: {count}",
"reset_offers_done_one": "1 úkol nyní při dokončení resetuje čítač",
"setups_before_after": "Úkoly u objektu: {before} → {after}",
"setups_new_object_count": "Nový objekt „{name}“ · úkoly: {count}",
"schedule_preview_title": "Nejbližší termíny",
@@ -1046,6 +1138,7 @@
"history_auto": "Automaticky",
"battery_fleet_title": "Flotila baterií",
"battery_fleet_none_low": "Všechny baterie OK — není co měnit.",
"battery_fleet_progress": "K výměně: {n}",
"battery_fleet_buy_now": "Koupit nyní",
"battery_fleet_soon": "Brzy potřeba",
"battery_fleet_soon_hint": "Odhad podle poslední výměny — objednejte předem.",
@@ -1099,8 +1192,12 @@
"dismiss": "Skrýt",
"gs_label": "Začínáme — tyto tipy zmizí, jak vaše nastavení poroste",
"gs_setups_chip": "Navrhovaná nastavení: nalezeno {n} zařízení s předpřipravenými spouštěči",
"gs_setups_chip_one": "Navrhovaná nastavení: nalezeno 1 zařízení s předpřipravenými spouštěči",
"gs_adopt_chip": "{n} problémových senzorů se může stát údržbovými úkoly",
"gs_adopt_chip_one": "1 problémový senzor se může stát údržbovým úkolem",
"gs_fleet_chip": "Jedno kliknutí nastaví flotilu baterií",
"gs_reset_chip": "{n} úkolů může resetovat čítač v integraci",
"gs_reset_chip_one": "1 úkol může resetovat čítač v integraci",
"cal_editor_window": "Výchozí okno",
"cal_editor_window_week": "Týden (7 dní)",
"cal_editor_window_fortnight": "Dva týdny (14 dní)",
@@ -243,6 +243,26 @@
"sort_task_name": "Opgavenavn",
"all_objects": "Alle objekter",
"all_parts": "Alle dele",
"all_areas": "Alle områder",
"areas_filter": "Filtrér områder…",
"areas_filter_none": "Intet område matcher filteret.",
"area_cost_year": "Omkostning {year}",
"area_object_count": "{n} objekter",
"area_object_count_one": "1 objekt",
"area_range_12m": "Seneste 12 måneder",
"area_range_all": "Hele perioden",
"area_task_filter": "Filtrér efter opgave…",
"area_kpi_completions": "Udførelser",
"area_kpi_total_time": "Samlet tid",
"area_cost_per_month": "Omkostning pr. måned",
"area_cost_per_year": "Omkostning pr. år",
"area_cost_per_object": "Omkostning pr. objekt",
"area_cost_share": "Andel",
"area_history_section": "Historik (alle objekter)",
"area_report_print": "Områderapport (PDF)",
"area_report_title": "Områderapport",
"area_report_period": "Periode",
"area_no_costs": "Ingen omkostninger registreret i denne periode.",
"no_tasks_yet": "Ingen opgaver endnu",
"add_first_task": "Tilføj første opgave",
"trigger_configuration": "Udløserkonfiguration",
@@ -401,11 +421,70 @@
"qr_download": "Download SVG",
"qr_action_view": "Vis vedligeholdelsesoplysninger",
"qr_action_complete": "Marker vedligeholdelse som fuldført",
"qr_action_quick_complete": "Hurtig-fuldfør — uden dialog",
"qr_print_quick_none": "Hurtig-fuldfør-koder kræver standardværdier for hurtig-fuldfør på en opgave (opgavedialogen → Standardværdier for hurtig-fuldfør) — ingen opgave i de valgte objekter har nogen endnu.",
"qr_url_mode": "Linktype",
"qr_mode_companion": "Companion-app",
"qr_mode_local": "Lokal (mDNS)",
"qr_mode_server": "Server-URL",
"overview": "Oversigt",
"strat_this_month": "Denne måned",
"strat_later": "Senere",
"strat_week": "Uge",
"strat_fortnight": "To uger",
"strat_month": "Måned",
"strat_year": "År",
"strat_other": "Andet",
"strat_status": "Tilstand",
"strat_empty_title": "Ingen vedligeholdelsesobjekter endnu",
"strat_empty_content": "Åbn Vedligeholdelse-panelet for at tilføje dit første objekt — poolpumpe, ventilationsfilter, køretøj, alt der har brug for planlagt pleje.",
"strat_open_panel": "Åbn Vedligeholdelse-panelet",
"strat_add_object": "Tilføj objekt",
"strat_not_loaded": "**Maintenance Supporter** er ikke indlæst. Installer/aktivér integrationen først.",
"strat_group_by": "Gruppér visninger efter",
"strat_group_area": "Efter område (standard)",
"strat_group_status": "Efter status (Forfalden / Udløst / Snart forfalden / OK)",
"strat_group_floor": "Efter etage (bruger HA-etagerne)",
"strat_group_due_date": "Efter forfaldsdato (Forfalden / I dag / Denne uge / Denne måned / Senere)",
"strat_group_calendar": "Rullende kalender (Uge / To uger / Måned / År)",
"strat_editor_help": "Visningen \"Oversigt\" er altid først. Tomme grupper springes over.",
"picker_name_calendar": "Maintenance Supporter — Kalender",
"picker_name_panel": "Maintenance Supporter — Panel som kort",
"picker_name_budget": "Maintenance Supporter — Vedligeholdelsesbudget",
"picker_name_groups": "Maintenance Supporter — Grupper",
"picker_name_vacation": "Maintenance Supporter — Ferietilstand",
"picker_name_battery_fleet": "Batteriflåde",
"picker_name_section": "Maintenance Supporter — Sektion",
"picker_name_vacation_status": "Maintenance Supporter — Feriestatus",
"picker_name_budget_status": "Maintenance Supporter — Budgetstatus",
"picker_desc_card": "Oversigt over dine vedligeholdelsesopgaver med hurtighandlinger.",
"picker_desc_calendar": "Rullende kalender over vedligeholdelsesopgaver med vinduer på 7/14/30/365 dage, kildeikoner og mærker for prognosesikkerhed.",
"picker_desc_panel": "Hele Maintenance Supporter-panelet som kort — til en panelvisning / dashboard-undervisning uden punkt i sidebjælken.",
"picker_desc_budget": "Rediger måneds- og årsbudgettet direkte på dashboardet.",
"picker_desc_groups": "Opret, omdøb og slet opgavegrupper direkte på dashboardet.",
"picker_desc_vacation": "Slå ferietilstand til eller fra, og angiv datoerne direkte på dashboardet.",
"picker_desc_battery_fleet": "Alle overvågede batterier: hvad der er lavt nu, hvad der snart løber tør, og hvad der skal købes.",
"picker_desc_dashboard": "Automatisk genereret dashboard. Gruppér visninger efter område, status, etage eller forfaldsdato — vælges i strategieditoren eller i YAML.",
"picker_desc_section": "Indlejr vedligeholdelsesopgaver (kan filtreres efter område, status, forfaldsdato) som en sektion i enhver dashboardvisning.",
"picker_desc_vacation_status": "Kompakt statusbanner for ferietilstand. Tryk for at åbne indstillingerne i panelet.",
"picker_desc_budget_status": "Oversigt over måneds- og årsbudgettet til vedligeholdelse. Tryk for detaljer.",
"picker_desc_groups_status": "Liste over konfigurerede opgavegrupper med antal opgaver i hver.",
"hist_note_button": "Fuldført via knap på dashboardet",
"hist_note_todo": "Fuldført fra to-do-listen",
"hist_note_voice": "Fuldført med stemmen",
"hist_note_notification": "Fuldført fra notifikationen",
"hist_note_nfc": "Fuldført via NFC-tag",
"hist_note_shopping_list": "Fuldført fra indkøbslisten",
"hist_note_todo_mirror": "Fuldført fra en spejlet to-do-liste",
"hist_note_skipped_notification": "Sprunget over fra notifikationen",
"hist_note_skipped_button": "Sprunget over via knap på dashboardet",
"hist_note_sensor_triggered": "Sensorudløser aktiveret",
"hist_note_initial": "Startværdi angivet ved oprettelse af opgaven",
"hist_note_auto_recovered": "Automatisk fuldført: sensoren er normal igen ({value})",
"hist_note_reset": "Nulstillet til {date}",
"hist_note_trigger_replaced": "Udløserentitet erstattet: {old} → {new}",
"hist_note_trigger_removed": "Sensorudløser fjernet (entitet: {entity}). Tidsplan ændret til {schedule}.",
"hist_note_compound_removed": "Entiteten {entity} fjernet fra den sammensatte udløser; resterende betingelser: {count}.",
"recent_activities": "Seneste aktiviteter",
"search_notes": "Søg i noter",
"avg_cost": "Gns. omkostning",
@@ -703,6 +782,7 @@
"on_complete_action_desc": "Kalder en HA-tjeneste, når opgaven fuldføres — f.eks. nulstil en tæller på enheden.",
"on_complete_action_target": "Målenhed",
"on_complete_action_target_hint": "Bemærk: enhedens domæne skal matche tjenesten — f.eks. virker 'button.press' kun på button.*, 'counter.increment' kun på counter.*, 'input_button.press' kun på input_button.* osv. Ved et misforhold mislykkes handlingen lydløst (HA logger 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Ikke når opgaven fuldføres af sig selv (dens sensor er normal igen)",
"on_complete_action_data": "Data (JSON, valgfrit)",
"on_complete_action_test": "Valider konfiguration",
"on_complete_action_test_success": "✓ Konfiguration gyldig (handlingen udløses kun ved fuldførelse af opgaven)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "\"{name}\": opgaver {before} → {after}",
"adopt_problem_adopt": "Adopter valgte",
"adopt_problem_done": "Adopterede {tasks} problemsensor(er)",
"adopt_problem_done_one": "Adopterede 1 problemsensor",
"views_label": "Visninger",
"views_none": "— Ingen visning —",
"views_manage": "Gem / administrer visninger",
@@ -1013,6 +1094,7 @@
"setups_none": "Ingen understøttede enheder med utilkoblede forbrugsstof-sensorer fundet.",
"setups_adopt": "Opsæt valgte",
"setups_done": "{tasks} sensor-koblede opgaver oprettet.",
"setups_done_one": "1 sensor-koblet opgave oprettet.",
"complete_parts_used": "Dele brugt denne gang",
"part_delete_confirm": "Slet delen '{name}'? Lagersporing, opgavekoblinger og en åben købspåmindelse fjernes.",
"baseline_start_value": "Startværdi (valgfrit)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "samme model på en anden enhed",
"setups_maybe_covered": "Findes sandsynligvis allerede som \"{name}\"",
"setups_already": "Findes allerede: {names}",
"setups_reset_line": "Fuldførelse nulstiller også tælleren i integrationen (“{button}”)",
"setups_reset_enable": "— knappen aktiveres til dette formål",
"reset_offers_title": "Nulstil tællere ved fuldførelse",
"reset_offers_hint": "Disse opgaver overvåger en tæller, som integrationen selv fører. Når de er forbundet, trykker en fuldførelse her også på integrationens nulstillingsknap — ellers kører tælleren videre, og opgaven forfalder straks igen.",
"reset_offers_before": "Nu: fuldførelse lader tælleren i {integration} køre videre",
"reset_offers_after": "Derefter: trykker også på “{button}”",
"reset_offers_renamed": "Omdøbt siden opsætningen — tjek, at opgaven hører til denne del",
"reset_offers_apply": "Forbind nulstillinger",
"reset_offers_done": "{count} opgaver nulstiller nu deres tæller ved fuldførelse",
"reset_offers_done_one": "1 opgave nulstiller nu sin tæller ved fuldførelse",
"setups_before_after": "Opgaver på objektet: {before} → {after}",
"setups_new_object_count": "Nyt objekt \"{name}\" · opgaver: {count}",
"schedule_preview_title": "Kommende datoer",
@@ -1046,6 +1138,7 @@
"history_auto": "Automatisk",
"battery_fleet_title": "Batteriflåde",
"battery_fleet_none_low": "Alle batterier OK — intet at skifte.",
"battery_fleet_progress": "{n} skal skiftes",
"battery_fleet_buy_now": "Køb nu",
"battery_fleet_soon": "Nødvendigt snart",
"battery_fleet_soon_hint": "Estimeret ud fra sidste skift — bestil på forhånd.",
@@ -1099,8 +1192,12 @@
"dismiss": "Afvis",
"gs_label": "Kom godt i gang — disse tips forsvinder, efterhånden som opsætningen vokser",
"gs_setups_chip": "Foreslåede opsætninger fandt {n} enheder med forudindstillede udløsere",
"gs_setups_chip_one": "Foreslåede opsætninger fandt 1 enhed med forudindstillede udløsere",
"gs_adopt_chip": "{n} problemsensorer kan blive vedligeholdelsesopgaver",
"gs_adopt_chip_one": "1 problemsensor kan blive en vedligeholdelsesopgave",
"gs_fleet_chip": "Ét klik opsætter batteriflåden",
"gs_reset_chip": "{n} opgaver kan nulstille deres tæller i integrationen",
"gs_reset_chip_one": "1 opgave kan nulstille sin tæller i integrationen",
"cal_editor_window": "Standardvindue",
"cal_editor_window_week": "Uge (7 dage)",
"cal_editor_window_fortnight": "To uger (14 dage)",
@@ -243,6 +243,26 @@
"sort_task_name": "Aufgaben-Name",
"all_objects": "Alle Objekte",
"all_parts": "Alle Teile",
"all_areas": "Alle Bereiche",
"areas_filter": "Bereiche filtern…",
"areas_filter_none": "Kein Bereich passt zum Filter.",
"area_cost_year": "Kosten {year}",
"area_object_count": "{n} Objekte",
"area_object_count_one": "1 Objekt",
"area_range_12m": "Letzte 12 Monate",
"area_range_all": "Gesamter Zeitraum",
"area_task_filter": "Nach Aufgabe filtern…",
"area_kpi_completions": "Erledigungen",
"area_kpi_total_time": "Gesamtzeit",
"area_cost_per_month": "Kosten pro Monat",
"area_cost_per_year": "Kosten pro Jahr",
"area_cost_per_object": "Kosten pro Objekt",
"area_cost_share": "Anteil",
"area_history_section": "Verlauf (alle Objekte)",
"area_report_print": "Bereichsbericht (PDF)",
"area_report_title": "Bereichsbericht",
"area_report_period": "Zeitraum",
"area_no_costs": "Keine Kosten in diesem Zeitraum erfasst.",
"no_tasks_yet": "Noch keine Aufgaben",
"add_first_task": "Erste Aufgabe hinzufügen",
"trigger_configuration": "Trigger-Konfiguration",
@@ -401,11 +421,70 @@
"qr_download": "SVG herunterladen",
"qr_action_view": "Wartungsinfo anzeigen",
"qr_action_complete": "Wartung als erledigt markieren",
"qr_action_quick_complete": "Schnell-Abschluss — ohne Dialog",
"qr_print_quick_none": "Schnell-Abschluss-Codes brauchen Schnell-Abschluss-Standardwerte an einer Aufgabe (Aufgabendialog → Schnell-Abschluss-Standardwerte) — noch keine Aufgabe der gewählten Objekte hat welche.",
"qr_url_mode": "Link-Typ",
"qr_mode_companion": "Companion App",
"qr_mode_local": "Lokal (mDNS)",
"qr_mode_server": "Server-URL",
"overview": "Übersicht",
"strat_this_month": "Diesen Monat",
"strat_later": "Später",
"strat_week": "Woche",
"strat_fortnight": "Zwei Wochen",
"strat_month": "Monat",
"strat_year": "Jahr",
"strat_other": "Sonstige",
"strat_status": "Aktueller Stand",
"strat_empty_title": "Noch keine Wartungsobjekte",
"strat_empty_content": "Öffne das Wartungspanel und füge dein erstes Objekt hinzu — Poolpumpe, Lüftungsfilter, Fahrzeug, alles, was regelmäßige Pflege braucht.",
"strat_open_panel": "Wartungspanel öffnen",
"strat_add_object": "Objekt hinzufügen",
"strat_not_loaded": "**Maintenance Supporter** ist nicht geladen. Installiere/aktiviere zuerst die Integration.",
"strat_group_by": "Ansichten gruppieren nach",
"strat_group_area": "Nach Bereich (Standard)",
"strat_group_status": "Nach Status (Überfällig / Ausgelöst / Bald fällig / OK)",
"strat_group_floor": "Nach Etage (nutzt die HA-Etagen)",
"strat_group_due_date": "Nach Fälligkeit (Überfällig / Heute / Diese Woche / Diesen Monat / Später)",
"strat_group_calendar": "Rollierender Kalender (Woche / Zwei Wochen / Monat / Jahr)",
"strat_editor_help": "Die Ansicht „Übersicht“ steht immer an erster Stelle. Leere Gruppen werden ausgelassen.",
"picker_name_calendar": "Maintenance Supporter — Kalender",
"picker_name_panel": "Maintenance Supporter — Panel als Karte",
"picker_name_budget": "Maintenance Supporter — Wartungsbudget",
"picker_name_groups": "Maintenance Supporter — Gruppen",
"picker_name_vacation": "Maintenance Supporter — Urlaubsmodus",
"picker_name_battery_fleet": "Batterie-Flotte",
"picker_name_section": "Maintenance Supporter — Abschnitt",
"picker_name_vacation_status": "Maintenance Supporter — Urlaubsstatus",
"picker_name_budget_status": "Maintenance Supporter — Budgetstatus",
"picker_desc_card": "Übersicht deiner Wartungsaufgaben mit Schnellaktionen.",
"picker_desc_calendar": "Rollierender Kalender der Wartungsaufgaben mit 7/14/30/365-Tage-Fenstern, Quellen-Icons und Badges zur Vorhersage-Genauigkeit.",
"picker_desc_panel": "Das komplette Maintenance-Supporter-Panel als Karte — für eine Panel-Ansicht / Dashboard-Unteransicht ohne Eintrag in der Seitenleiste.",
"picker_desc_budget": "Monats- und Jahresbudget direkt im Dashboard bearbeiten.",
"picker_desc_groups": "Aufgabengruppen direkt im Dashboard anlegen, umbenennen und löschen.",
"picker_desc_vacation": "Urlaubsmodus direkt im Dashboard ein- und ausschalten und den Zeitraum festlegen.",
"picker_desc_battery_fleet": "Alle überwachten Batterien: was jetzt schwach ist, was bald leer wird und was du kaufen musst.",
"picker_desc_dashboard": "Automatisch erzeugtes Dashboard. Ansichten nach Bereich, Status, Etage oder Fälligkeit gruppieren — wählbar im Strategie-Editor oder per YAML.",
"picker_desc_section": "Wartungsaufgaben (filterbar nach Bereich, Status, Fälligkeit) als Abschnitt in jede Dashboard-Ansicht einbetten.",
"picker_desc_vacation_status": "Kompaktes Status-Banner für den Urlaubsmodus. Antippen öffnet die Einstellungen im Panel.",
"picker_desc_budget_status": "Übersicht über Monats- und Jahresbudget für die Wartung. Antippen für Details.",
"picker_desc_groups_status": "Liste der eingerichteten Aufgabengruppen mit der Anzahl ihrer Aufgaben.",
"hist_note_button": "Über Dashboard-Button abgeschlossen",
"hist_note_todo": "Über die To-do-Liste abgeschlossen",
"hist_note_voice": "Per Sprachbefehl abgeschlossen",
"hist_note_notification": "Über die Benachrichtigung abgeschlossen",
"hist_note_nfc": "Per NFC-Tag abgeschlossen",
"hist_note_shopping_list": "Über die Einkaufsliste abgeschlossen",
"hist_note_todo_mirror": "Über eine gespiegelte To-do-Liste abgeschlossen",
"hist_note_skipped_notification": "Über die Benachrichtigung übersprungen",
"hist_note_skipped_button": "Über Dashboard-Button übersprungen",
"hist_note_sensor_triggered": "Sensor-Trigger ausgelöst",
"hist_note_initial": "Anfangswert bei der Aufgabenerstellung gesetzt",
"hist_note_auto_recovered": "Automatisch abgeschlossen: Sensor hat sich erholt ({value})",
"hist_note_reset": "Zurückgesetzt auf {date}",
"hist_note_trigger_replaced": "Trigger-Entität ersetzt: {old} → {new}",
"hist_note_trigger_removed": "Sensor-Trigger entfernt (Entität war: {entity}). Zeitplan umgestellt auf {schedule}.",
"hist_note_compound_removed": "Entität {entity} aus dem verknüpften Trigger entfernt; verbleibende Bedingungen: {count}.",
"recent_activities": "Letzte Aktivitäten",
"search_notes": "Notizen durchsuchen",
"avg_cost": "Ø Kosten",
@@ -703,6 +782,7 @@
"on_complete_action_desc": "Ruft beim Erledigen der Aufgabe einen HA-Service auf — z. B. einen Zähler am Gerät zurücksetzen.",
"on_complete_action_target": "Ziel-Entität",
"on_complete_action_target_hint": "Achtung: Domain der Entität muss zum Service passen — z. B. 'button.press' nur für button.*, 'counter.increment' nur für counter.*, 'input_button.press' nur für input_button.* etc. Bei Mismatch fired die Aktion nicht (HA loggt 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Nicht, wenn sich die Aufgabe selbst erledigt (ihr Sensor hat sich erholt)",
"on_complete_action_data": "Daten (JSON, optional)",
"on_complete_action_test": "Konfiguration prüfen",
"on_complete_action_test_success": "✓ Konfiguration gültig (Aktion wird erst beim Abschluss ausgeführt)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "„{name}“: Aufgaben {before} → {after}",
"adopt_problem_adopt": "Auswahl übernehmen",
"adopt_problem_done": "{tasks} Problem-Sensor(en) übernommen",
"adopt_problem_done_one": "1 Problem-Sensor übernommen",
"views_label": "Ansichten",
"views_none": "— Keine Ansicht —",
"views_manage": "Ansichten speichern/verwalten",
@@ -1013,6 +1094,7 @@
"setups_none": "Keine unterstützten Geräte mit unverdrahteten Verbrauchsmaterial-Sensoren gefunden.",
"setups_adopt": "Auswahl einrichten",
"setups_done": "{tasks} sensor-verdrahtete Aufgaben angelegt.",
"setups_done_one": "1 sensor-verdrahtete Aufgabe angelegt.",
"complete_parts_used": "Diesmal verwendete Teile",
"part_delete_confirm": "Ersatzteil '{name}' löschen? Bestandsverfolgung, Aufgaben-Verknüpfungen und eine offene Kauf-Erinnerung werden entfernt.",
"baseline_start_value": "Startwert (optional)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "gleiches Modell an einem anderen Gerät",
"setups_maybe_covered": "Wahrscheinlich schon vorhanden als „{name}“",
"setups_already": "Schon vorhanden: {names}",
"setups_reset_line": "Erledigen setzt auch den Zähler in der Integration zurück („{button}“)",
"setups_reset_enable": "— der Button wird dafür eingeschaltet",
"reset_offers_title": "Zähler beim Erledigen zurücksetzen",
"reset_offers_hint": "Diese Aufgaben beobachten einen Zähler, den die Integration selbst führt. Verbunden drückt das Erledigen hier auch den Reset-Button der Integration — sonst läuft der Zähler weiter und die Aufgabe ist sofort wieder fällig.",
"reset_offers_before": "Jetzt: Erledigen lässt den Zähler in {integration} weiterlaufen",
"reset_offers_after": "Dann: Erledigen drückt auch „{button}“",
"reset_offers_renamed": "Seit der Einrichtung umbenannt — prüfe, ob diese Aufgabe zu diesem Teil gehört",
"reset_offers_apply": "Reset-Buttons verbinden",
"reset_offers_done": "{count} Aufgaben setzen ihren Zähler jetzt beim Erledigen zurück",
"reset_offers_done_one": "1 Aufgabe setzt ihren Zähler jetzt beim Erledigen zurück",
"setups_before_after": "Aufgaben am Objekt: {before} → {after}",
"setups_new_object_count": "Neues Objekt „{name}“ · Aufgaben: {count}",
"schedule_preview_title": "Nächste Termine",
@@ -1046,6 +1138,7 @@
"history_auto": "Automatisch",
"battery_fleet_title": "Batterie-Flotte",
"battery_fleet_none_low": "Alle Batterien OK — nichts zu wechseln.",
"battery_fleet_progress": "{n} zu wechseln",
"battery_fleet_buy_now": "Jetzt kaufen",
"battery_fleet_soon": "Bald nötig",
"battery_fleet_soon_hint": "Aus dem letzten Wechseldatum geschätzt — vorab bestellen.",
@@ -1099,8 +1192,12 @@
"dismiss": "Ausblenden",
"gs_label": "Erste Schritte — diese Hinweise verschwinden, wenn dein Setup wächst",
"gs_setups_chip": "Vorgeschlagene Setups: {n} Geräte mit vorverdrahteten Auslösern gefunden",
"gs_setups_chip_one": "Vorgeschlagene Setups: 1 Gerät mit vorverdrahteten Auslösern gefunden",
"gs_adopt_chip": "{n} Problem-Sensoren können zu Wartungsaufgaben werden",
"gs_adopt_chip_one": "1 Problem-Sensor kann zu einer Wartungsaufgabe werden",
"gs_fleet_chip": "Ein Klick richtet die Batterieflotte ein",
"gs_reset_chip": "{n} Aufgaben können ihren Zähler in der Integration zurücksetzen",
"gs_reset_chip_one": "1 Aufgabe kann ihren Zähler in der Integration zurücksetzen",
"cal_editor_window": "Standard-Zeitfenster",
"cal_editor_window_week": "Woche (7 Tage)",
"cal_editor_window_fortnight": "Zwei Wochen (14 Tage)",
@@ -243,6 +243,26 @@
"sort_task_name": "Task name",
"all_objects": "All objects",
"all_parts": "All parts",
"all_areas": "All areas",
"areas_filter": "Filter areas…",
"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…",
"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",
@@ -401,11 +421,70 @@
"qr_download": "Download SVG",
"qr_action_view": "View maintenance info",
"qr_action_complete": "Mark maintenance as complete",
"qr_action_quick_complete": "Quick-complete — no dialog",
"qr_print_quick_none": "Quick-complete codes need quick-complete defaults on a task (task dialog → Quick-complete defaults) — 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 — 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 — Calendar",
"picker_name_panel": "Maintenance Supporter — Panel",
"picker_name_budget": "Maintenance Supporter — Budget",
"picker_name_groups": "Maintenance Supporter — Groups",
"picker_name_vacation": "Maintenance Supporter — Vacation",
"picker_name_battery_fleet": "Battery Fleet",
"picker_name_section": "Maintenance Supporter — Section",
"picker_name_vacation_status": "Maintenance Supporter — Vacation Status",
"picker_name_budget_status": "Maintenance Supporter — 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 — 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 — 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} → {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",
@@ -709,6 +788,7 @@
"on_complete_action_desc": "Calls an HA service when the task is completed — 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 — 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_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": "✓ Configuration valid (action will fire only on task completion)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "\"{name}\": tasks {before} → {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": "— No view —",
"views_manage": "Save / manage views",
@@ -1013,6 +1094,7 @@
"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)",
@@ -1035,6 +1117,16 @@
"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 (“{button}”)",
"setups_reset_enable": "— 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 — 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 “{button}”",
"reset_offers_renamed": "Renamed since it was set up — 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} → {after}",
"setups_new_object_count": "New object \"{name}\" · tasks: {count}",
"schedule_preview_title": "Next dates",
@@ -1046,6 +1138,7 @@
"history_auto": "Automatic",
"battery_fleet_title": "Battery fleet",
"battery_fleet_none_low": "All batteries OK — 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 — order ahead.",
@@ -1099,8 +1192,12 @@
"dismiss": "Dismiss",
"gs_label": "Getting started — 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)",
@@ -241,6 +241,26 @@
"sort_task_name": "Nombre de la tarea",
"all_objects": "Todos los objetos",
"all_parts": "Todas las piezas",
"all_areas": "Todas las áreas",
"areas_filter": "Filtrar áreas…",
"areas_filter_none": "Ningún área coincide con el filtro.",
"area_cost_year": "Coste {year}",
"area_object_count": "{n} objetos",
"area_object_count_one": "1 objeto",
"area_range_12m": "Últimos 12 meses",
"area_range_all": "Todo el periodo",
"area_task_filter": "Filtrar por tarea…",
"area_kpi_completions": "Realizaciones",
"area_kpi_total_time": "Tiempo total",
"area_cost_per_month": "Coste por mes",
"area_cost_per_year": "Coste por año",
"area_cost_per_object": "Coste por objeto",
"area_cost_share": "Proporción",
"area_history_section": "Historial (todos los objetos)",
"area_report_print": "Informe del área (PDF)",
"area_report_title": "Informe del área",
"area_report_period": "Periodo",
"area_no_costs": "No hay costes registrados en este periodo.",
"no_tasks_yet": "Aún no hay tareas",
"add_first_task": "Agregar primera tarea",
"trigger_configuration": "Configuración del disparador",
@@ -399,11 +419,70 @@
"qr_download": "Descargar SVG",
"qr_action_view": "Ver info de mantenimiento",
"qr_action_complete": "Marcar mantenimiento como completado",
"qr_action_quick_complete": "Finalización rápida — sin diálogo",
"qr_print_quick_none": "Los códigos de finalización rápida necesitan valores predeterminados de finalización rápida en una tarea (diálogo de la tarea → Valores predeterminados de finalización rápida); ninguna tarea de los objetos elegidos los tiene todavía.",
"qr_url_mode": "Tipo de enlace",
"qr_mode_companion": "Companion App",
"qr_mode_local": "Local (mDNS)",
"qr_mode_server": "URL del servidor",
"overview": "Resumen",
"strat_this_month": "Este mes",
"strat_later": "Más adelante",
"strat_week": "Semana",
"strat_fortnight": "Quincena",
"strat_month": "Mes",
"strat_year": "Año",
"strat_other": "Otros",
"strat_status": "Estado",
"strat_empty_title": "Aún no hay objetos de mantenimiento",
"strat_empty_content": "Abre el panel de Mantenimiento para añadir tu primer objeto — bomba de piscina, filtro de climatización, vehículo, cualquier cosa que necesite un cuidado programado.",
"strat_open_panel": "Abrir el panel de Mantenimiento",
"strat_add_object": "Añadir objeto",
"strat_not_loaded": "**Maintenance Supporter** no está cargado. Instala/activa primero la integración.",
"strat_group_by": "Agrupar vistas por",
"strat_group_area": "Por área (predeterminado)",
"strat_group_status": "Por estado (Vencida / Activada / Próxima / OK)",
"strat_group_floor": "Por planta (usa las plantas de HA)",
"strat_group_due_date": "Por vencimiento (Vencida / Hoy / Esta semana / Este mes / Más adelante)",
"strat_group_calendar": "Calendario móvil (Semana / Quincena / Mes / Año)",
"strat_editor_help": "La vista «Resumen» siempre va primero. Los grupos vacíos se omiten.",
"picker_name_calendar": "Maintenance Supporter — Calendario",
"picker_name_panel": "Maintenance Supporter — Panel como tarjeta",
"picker_name_budget": "Maintenance Supporter — Presupuesto de mantenimiento",
"picker_name_groups": "Maintenance Supporter — Grupos",
"picker_name_vacation": "Maintenance Supporter — Modo vacaciones",
"picker_name_battery_fleet": "Flota de baterías",
"picker_name_section": "Maintenance Supporter — Sección",
"picker_name_vacation_status": "Maintenance Supporter — Estado de vacaciones",
"picker_name_budget_status": "Maintenance Supporter — Estado del presupuesto",
"picker_desc_card": "Resumen de tus tareas de mantenimiento con acciones rápidas.",
"picker_desc_calendar": "Calendario móvil de tareas de mantenimiento con ventanas de 7/14/30/365 días, iconos de origen y etiquetas de confianza de la predicción.",
"picker_desc_panel": "El panel completo de Maintenance Supporter como tarjeta — para una vista de tipo panel o una subvista, sin entrada en la barra lateral.",
"picker_desc_budget": "Edita el presupuesto mensual y anual directamente en el panel.",
"picker_desc_groups": "Crea, renombra y elimina grupos de tareas directamente en el panel.",
"picker_desc_vacation": "Activa o desactiva el modo vacaciones y define sus fechas directamente en el panel.",
"picker_desc_battery_fleet": "Todas las pilas monitorizadas: cuáles están bajas ahora, cuáles se agotarán pronto y qué comprar.",
"picker_desc_dashboard": "Panel generado automáticamente. Agrupa las vistas por área, estado, planta o vencimiento — se elige en el editor de la estrategia o en YAML.",
"picker_desc_section": "Inserta las tareas de mantenimiento (filtrables por área, estado y vencimiento) como sección en cualquier vista del panel.",
"picker_desc_vacation_status": "Banner compacto con el estado del modo vacaciones. Toca para abrir los ajustes en el panel.",
"picker_desc_budget_status": "Resumen del presupuesto de mantenimiento mensual y anual. Toca para ver detalles.",
"picker_desc_groups_status": "Lista de los grupos de tareas configurados con el número de tareas de cada uno.",
"hist_note_button": "Completada desde un botón del panel",
"hist_note_todo": "Completada desde la lista de tareas",
"hist_note_voice": "Completada por voz",
"hist_note_notification": "Completada desde la notificación",
"hist_note_nfc": "Completada con una etiqueta NFC",
"hist_note_shopping_list": "Completada desde la lista de la compra",
"hist_note_todo_mirror": "Completada desde una lista de tareas reflejada",
"hist_note_skipped_notification": "Omitida desde la notificación",
"hist_note_skipped_button": "Omitida desde un botón del panel",
"hist_note_sensor_triggered": "Disparador de sensor activado",
"hist_note_initial": "Valor inicial establecido al crear la tarea",
"hist_note_auto_recovered": "Completada automáticamente: el sensor se recuperó ({value})",
"hist_note_reset": "Restablecida al {date}",
"hist_note_trigger_replaced": "Entidad disparadora reemplazada: {old} → {new}",
"hist_note_trigger_removed": "Disparador de sensor eliminado (entidad: {entity}). Programación convertida a {schedule}.",
"hist_note_compound_removed": "Entidad {entity} eliminada del disparador compuesto; condiciones restantes: {count}.",
"recent_activities": "Actividades recientes",
"search_notes": "Buscar en notas",
"avg_cost": "Ø Coste",
@@ -716,6 +795,7 @@
"groups_manage_tasks": "Gestionar asignaciones de tareas…",
"groups_open_panel": "Abrir en el panel",
"on_complete_action_target_hint": "Nota: el dominio de la entidad debe coincidir con el servicio — p. ej. 'button.press' solo funciona en button.*, 'counter.increment' solo en counter.*, 'input_button.press' solo en input_button.* etc. Si no coinciden, la acción falla silenciosamente (HA registra 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "No cuando la tarea se completa sola (su sensor se recuperó)",
"show_all_objects": "Mostrar todos los objetos",
"show_all_tasks": "Quitar filtro — mostrar todas las tareas",
"filter_to_overdue": "Filtrar la lista solo a vencidas",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "«{name}»: tareas {before} → {after}",
"adopt_problem_adopt": "Adoptar seleccionados",
"adopt_problem_done": "{tasks} sensor(es) de problema adoptado(s)",
"adopt_problem_done_one": "1 sensor de problema adoptado",
"views_label": "Vistas",
"views_none": "— Sin vista —",
"views_manage": "Guardar / gestionar vistas",
@@ -1013,6 +1094,7 @@
"setups_none": "No se encontraron dispositivos compatibles con sensores de consumibles sin conectar.",
"setups_adopt": "Configurar selección",
"setups_done": "{tasks} tareas conectadas a sensores creadas.",
"setups_done_one": "1 tarea conectada a sensores creada.",
"complete_parts_used": "Piezas usadas esta vez",
"part_delete_confirm": "¿Eliminar la pieza '{name}'? Se eliminarán su control de stock, los vínculos con tareas y cualquier recordatorio de compra abierto.",
"baseline_start_value": "Valor inicial (opcional)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "mismo modelo en otro dispositivo",
"setups_maybe_covered": "Probablemente ya existe como «{name}»",
"setups_already": "Ya existe: {names}",
"setups_reset_line": "Completarla también reinicia el contador en la integración («{button}»)",
"setups_reset_enable": "— para ello se activará el botón",
"reset_offers_title": "Reiniciar contadores al completar",
"reset_offers_hint": "Estas tareas vigilan un contador que lleva la propia integración. Una vez conectadas, completar la tarea aquí también pulsa el botón de reinicio de la integración — si no, el contador sigue corriendo y la tarea vuelve a vencer enseguida.",
"reset_offers_before": "Ahora: completarla deja en marcha el contador de {integration}",
"reset_offers_after": "Después: también pulsa «{button}»",
"reset_offers_renamed": "Renombrada desde su configuración — comprueba que esta tarea corresponde a esta pieza",
"reset_offers_apply": "Conectar reinicios",
"reset_offers_done": "{count} tareas reinician ahora su contador al completarse",
"reset_offers_done_one": "1 tarea reinicia ahora su contador al completarse",
"setups_before_after": "Tareas del objeto: {before} → {after}",
"setups_new_object_count": "Objeto nuevo «{name}» · tareas: {count}",
"schedule_preview_title": "Próximas fechas",
@@ -1046,6 +1138,7 @@
"history_auto": "Automático",
"battery_fleet_title": "Flota de baterías",
"battery_fleet_none_low": "Todas las baterías OK — nada que reemplazar.",
"battery_fleet_progress": "{n} por cambiar",
"battery_fleet_buy_now": "Comprar ahora",
"battery_fleet_soon": "Pronto necesario",
"battery_fleet_soon_hint": "Estimado según el último reemplazo — pide con antelación.",
@@ -1099,8 +1192,12 @@
"dismiss": "Descartar",
"gs_label": "Primeros pasos: estas sugerencias desaparecen a medida que crece tu configuración",
"gs_setups_chip": "Configuraciones sugeridas: {n} dispositivos con disparadores preconfigurados",
"gs_setups_chip_one": "Configuraciones sugeridas: 1 dispositivo con disparadores preconfigurados",
"gs_adopt_chip": "{n} sensores de problemas pueden convertirse en tareas de mantenimiento",
"gs_adopt_chip_one": "1 sensor de problemas puede convertirse en una tarea de mantenimiento",
"gs_fleet_chip": "Un clic configura la flota de baterías",
"gs_reset_chip": "{n} tareas pueden reiniciar su contador en la integración",
"gs_reset_chip_one": "1 tarea puede reiniciar su contador en la integración",
"cal_editor_window": "Ventana predeterminada",
"cal_editor_window_week": "Semana (7 días)",
"cal_editor_window_fortnight": "Quincena (14 días)",
@@ -243,6 +243,26 @@
"sort_task_name": "Tehtävän nimi",
"all_objects": "Kaikki kohteet",
"all_parts": "Kaikki osat",
"all_areas": "Kaikki alueet",
"areas_filter": "Suodata alueita…",
"areas_filter_none": "Mikään alue ei vastaa suodatinta.",
"area_cost_year": "Kustannus {year}",
"area_object_count": "{n} kohdetta",
"area_object_count_one": "1 kohde",
"area_range_12m": "Viimeiset 12 kuukautta",
"area_range_all": "Koko ajalta",
"area_task_filter": "Suodata tehtävän mukaan…",
"area_kpi_completions": "Suoritukset",
"area_kpi_total_time": "Kokonaisaika",
"area_cost_per_month": "Kustannus kuukausittain",
"area_cost_per_year": "Kustannus vuosittain",
"area_cost_per_object": "Kustannus kohteittain",
"area_cost_share": "Osuus",
"area_history_section": "Historia (kaikki kohteet)",
"area_report_print": "Alueraportti (PDF)",
"area_report_title": "Alueraportti",
"area_report_period": "Ajanjakso",
"area_no_costs": "Tällä aikavälillä ei ole kirjattu kustannuksia.",
"no_tasks_yet": "Ei vielä tehtäviä",
"add_first_task": "Lisää ensimmäinen tehtävä",
"trigger_configuration": "Laukaisimen asetukset",
@@ -401,11 +421,70 @@
"qr_download": "Lataa SVG",
"qr_action_view": "Näytä huoltotiedot",
"qr_action_complete": "Merkitse huolto valmiiksi",
"qr_action_quick_complete": "Pikasuoritus — ilman valintaikkunaa",
"qr_print_quick_none": "Pikasuorituskoodit vaativat tehtävälle pikasuorituksen oletukset (tehtävän valintaikkuna → Pikasuorituksen oletukset) — minkään valitun kohteen tehtävällä ei ole niitä vielä.",
"qr_url_mode": "Linkin tyyppi",
"qr_mode_companion": "Companion-sovellus",
"qr_mode_local": "Paikallinen (mDNS)",
"qr_mode_server": "Palvelimen URL",
"overview": "Yleiskatsaus",
"strat_this_month": "Tässä kuussa",
"strat_later": "Myöhemmin",
"strat_week": "Viikko",
"strat_fortnight": "Kaksi viikkoa",
"strat_month": "Kuukausi",
"strat_year": "Vuosi",
"strat_other": "Muut",
"strat_status": "Tila",
"strat_empty_title": "Ei vielä huoltokohteita",
"strat_empty_content": "Avaa Huolto-paneeli ja lisää ensimmäinen kohde — uima-altaan pumppu, ilmanvaihdon suodatin, ajoneuvo, mikä tahansa, joka tarvitsee säännöllistä huoltoa.",
"strat_open_panel": "Avaa Huolto-paneeli",
"strat_add_object": "Lisää kohde",
"strat_not_loaded": "**Maintenance Supporter** ei ole ladattuna. Asenna integraatio tai ota se ensin käyttöön.",
"strat_group_by": "Ryhmittele näkymät",
"strat_group_area": "Alueen mukaan (oletus)",
"strat_group_status": "Tilan mukaan (Myöhässä / Laukaistu / Pian erääntyy / OK)",
"strat_group_floor": "Kerroksen mukaan (käyttää HA:n kerroksia)",
"strat_group_due_date": "Eräpäivän mukaan (Myöhässä / Tänään / Tällä viikolla / Tässä kuussa / Myöhemmin)",
"strat_group_calendar": "Liukuva kalenteri (Viikko / Kaksi viikkoa / Kuukausi / Vuosi)",
"strat_editor_help": "”Yleiskatsaus”-näkymä on aina ensimmäisenä. Tyhjät ryhmät ohitetaan.",
"picker_name_calendar": "Maintenance Supporter — Kalenteri",
"picker_name_panel": "Maintenance Supporter — Paneeli korttina",
"picker_name_budget": "Maintenance Supporter — Huoltobudjetti",
"picker_name_groups": "Maintenance Supporter — Ryhmät",
"picker_name_vacation": "Maintenance Supporter — Lomatila",
"picker_name_battery_fleet": "Paristokanta",
"picker_name_section": "Maintenance Supporter — Osio",
"picker_name_vacation_status": "Maintenance Supporter — Loman tila",
"picker_name_budget_status": "Maintenance Supporter — Budjetin tila",
"picker_desc_card": "Yleiskatsaus huoltotehtäviisi pikatoiminnoilla.",
"picker_desc_calendar": "Huoltotehtävien liukuva kalenteri 7/14/30/365 päivän jaksoilla, lähdekuvakkeilla ja ennusteen luotettavuusmerkeillä.",
"picker_desc_panel": "Koko Maintenance Supporter -paneeli korttina — paneelinäkymään tai kojelaudan alinäkymään ilman sivupalkin kohtaa.",
"picker_desc_budget": "Muokkaa kuukausi- ja vuosibudjettia suoraan kojelaudalla.",
"picker_desc_groups": "Luo, nimeä uudelleen ja poista tehtäväryhmiä suoraan kojelaudalla.",
"picker_desc_vacation": "Ota lomatila käyttöön tai poista se käytöstä ja aseta sen päivät suoraan kojelaudalla.",
"picker_desc_battery_fleet": "Kaikki seuratut paristot: mitkä ovat nyt vähissä, mitkä loppuvat pian ja mitä pitää ostaa.",
"picker_desc_dashboard": "Automaattisesti luotu kojelauta. Ryhmittele näkymät alueen, tilan, kerroksen tai eräpäivän mukaan — valitaan strategiaeditorissa tai YAML:ssä.",
"picker_desc_section": "Upota huoltotehtävät (suodatettavissa alueen, tilan ja eräpäivän mukaan) osioksi mihin tahansa kojelaudan näkymään.",
"picker_desc_vacation_status": "Kompakti lomatilan tilabanneri. Napauta avataksesi asetukset paneelissa.",
"picker_desc_budget_status": "Kuukausi- ja vuosibudjetin yleiskatsaus huollolle. Napauta nähdäksesi tiedot.",
"picker_desc_groups_status": "Luettelo määritetyistä tehtäväryhmistä ja niiden tehtävien määrästä.",
"hist_note_button": "Suoritettu kojelaudan painikkeella",
"hist_note_todo": "Suoritettu tehtävälistasta",
"hist_note_voice": "Suoritettu puheella",
"hist_note_notification": "Suoritettu ilmoituksesta",
"hist_note_nfc": "Suoritettu NFC-tunnisteella",
"hist_note_shopping_list": "Suoritettu ostoslistasta",
"hist_note_todo_mirror": "Suoritettu peilatusta tehtävälistasta",
"hist_note_skipped_notification": "Ohitettu ilmoituksesta",
"hist_note_skipped_button": "Ohitettu kojelaudan painikkeella",
"hist_note_sensor_triggered": "Anturin laukaisin aktivoitui",
"hist_note_initial": "Alkuarvo asetettu tehtävän luonnissa",
"hist_note_auto_recovered": "Suoritettu automaattisesti: anturi palautui ({value})",
"hist_note_reset": "Nollattu päivään {date}",
"hist_note_trigger_replaced": "Laukaisimen entiteetti korvattu: {old} → {new}",
"hist_note_trigger_removed": "Anturin laukaisin poistettu (entiteetti: {entity}). Aikataulu muutettu: {schedule}.",
"hist_note_compound_removed": "Entiteetti {entity} poistettu yhdistetystä laukaisimesta; ehtoja jäljellä: {count}.",
"recent_activities": "Viimeaikaiset toimet",
"search_notes": "Hae muistiinpanoja",
"avg_cost": "Keskim. kustannus",
@@ -703,6 +782,7 @@
"on_complete_action_desc": "Kutsuu HA-palvelua, kun tehtävä suoritetaan — esim. nollaa laitteen laskuri.",
"on_complete_action_target": "Kohde-entiteetti",
"on_complete_action_target_hint": "Huom: entiteetin toimialueen on vastattava palvelua — esim. 'button.press' toimii vain button.*-entiteeteillä, 'counter.increment' vain counter.*-entiteeteillä, 'input_button.press' vain input_button.*-entiteeteillä jne. Epäsopivuuden sattuessa toiminto epäonnistuu hiljaisesti (HA kirjaa 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Ei, kun tehtävä suorittuu itsestään (sen anturi palautui)",
"on_complete_action_data": "Tiedot (JSON, valinnainen)",
"on_complete_action_test": "Tarkista asetukset",
"on_complete_action_test_success": "✓ Asetukset kelvolliset (toiminto laukeaa vain tehtävän suorituksen yhteydessä)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "\"{name}\": tehtävät {before} → {after}",
"adopt_problem_adopt": "Ota valitut käyttöön",
"adopt_problem_done": "Otettiin käyttöön {tasks} ongelma-anturia",
"adopt_problem_done_one": "Otettiin käyttöön 1 ongelma-anturi",
"views_label": "Näkymät",
"views_none": "— Ei näkymää —",
"views_manage": "Tallenna / hallitse näkymiä",
@@ -1013,6 +1094,7 @@
"setups_none": "Tuettuja laitteita, joissa on kytkemättömiä kulutusosa-antureita, ei löytynyt.",
"setups_adopt": "Ota valitut käyttöön",
"setups_done": "{tasks} anturiin kytkettyä tehtävää luotu.",
"setups_done_one": "1 anturiin kytketty tehtävä luotu.",
"complete_parts_used": "Tällä kertaa käytetyt osat",
"part_delete_confirm": "Poistetaanko osa '{name}'? Varastoseuranta, tehtäväkytkennät ja avoin ostomuistutus poistetaan.",
"baseline_start_value": "Aloitusarvo (valinnainen)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "sama malli toisessa laitteessa",
"setups_maybe_covered": "Todennäköisesti jo olemassa nimellä \"{name}\"",
"setups_already": "Jo olemassa: {names}",
"setups_reset_line": "Suoritus nollaa myös integraation laskurin (”{button}”)",
"setups_reset_enable": "— painike otetaan tätä varten käyttöön",
"reset_offers_title": "Nollaa laskurit suorituksen yhteydessä",
"reset_offers_hint": "Nämä tehtävät seuraavat laskuria, jota integraatio ylläpitää itse. Yhdistettynä tehtävän suorittaminen täällä painaa myös integraation nollauspainiketta — muuten laskuri jatkaa kulkuaan ja tehtävä erääntyy heti uudelleen.",
"reset_offers_before": "Nyt: suorituksen jälkeen {integration}-integraation laskuri jatkaa kulkuaan",
"reset_offers_after": "Sitten: painaa myös painiketta ”{button}”",
"reset_offers_renamed": "Nimetty uudelleen määrityksen jälkeen — tarkista, että tehtävä koskee tätä osaa",
"reset_offers_apply": "Yhdistä nollaukset",
"reset_offers_done": "{count} tehtävää nollaa nyt laskurinsa suorituksen yhteydessä",
"reset_offers_done_one": "1 tehtävä nollaa nyt laskurinsa suorituksen yhteydessä",
"setups_before_after": "Kohteen tehtävät: {before} → {after}",
"setups_new_object_count": "Uusi kohde \"{name}\" · tehtäviä: {count}",
"schedule_preview_title": "Seuraavat päivämäärät",
@@ -1046,6 +1138,7 @@
"history_auto": "Automaattinen",
"battery_fleet_title": "Paristokanta",
"battery_fleet_none_low": "Kaikki paristot OK — ei vaihdettavaa.",
"battery_fleet_progress": "Vaihdettavia: {n}",
"battery_fleet_buy_now": "Osta nyt",
"battery_fleet_soon": "Pian tarpeen",
"battery_fleet_soon_hint": "Arvioitu viime vaihdosta — tilaa etukäteen.",
@@ -1099,8 +1192,12 @@
"dismiss": "Hylkää",
"gs_label": "Aloitus — nämä vihjeet poistuvat asennuksen kasvaessa",
"gs_setups_chip": "Ehdotetut asetukset löysivät {n} laitetta valmiilla laukaisimilla",
"gs_setups_chip_one": "Ehdotetut asetukset löysivät 1 laitteen valmiilla laukaisimilla",
"gs_adopt_chip": "{n} ongelma-anturia voi muuttua huoltotehtäviksi",
"gs_adopt_chip_one": "1 ongelma-anturi voi muuttua huoltotehtäväksi",
"gs_fleet_chip": "Yksi napsautus määrittää akkukannan",
"gs_reset_chip": "{n} tehtävää voi nollata laskurinsa integraatiossa",
"gs_reset_chip_one": "1 tehtävä voi nollata laskurinsa integraatiossa",
"cal_editor_window": "Oletusikkuna",
"cal_editor_window_week": "Viikko (7 päivää)",
"cal_editor_window_fortnight": "Kaksi viikkoa (14 päivää)",
@@ -241,6 +241,26 @@
"sort_task_name": "Nom de la tâche",
"all_objects": "Tous les objets",
"all_parts": "Toutes les pièces",
"all_areas": "Toutes les zones",
"areas_filter": "Filtrer les zones…",
"areas_filter_none": "Aucune zone ne correspond au filtre.",
"area_cost_year": "Coût {year}",
"area_object_count": "{n} objets",
"area_object_count_one": "1 objet",
"area_range_12m": "12 derniers mois",
"area_range_all": "Toute la période",
"area_task_filter": "Filtrer par tâche…",
"area_kpi_completions": "Réalisations",
"area_kpi_total_time": "Temps total",
"area_cost_per_month": "Coût par mois",
"area_cost_per_year": "Coût par an",
"area_cost_per_object": "Coût par objet",
"area_cost_share": "Part",
"area_history_section": "Historique (tous les objets)",
"area_report_print": "Rapport de zone (PDF)",
"area_report_title": "Rapport de zone",
"area_report_period": "Période",
"area_no_costs": "Aucun coût enregistré sur cette période.",
"no_tasks_yet": "Pas encore de tâches",
"add_first_task": "Ajouter la première tâche",
"trigger_configuration": "Configuration du déclencheur",
@@ -399,11 +419,70 @@
"qr_download": "Télécharger SVG",
"qr_action_view": "Afficher les infos de maintenance",
"qr_action_complete": "Marquer la maintenance comme terminée",
"qr_action_quick_complete": "Finalisation rapide — sans fenêtre",
"qr_print_quick_none": "Les codes de finalisation rapide nécessitent des valeurs par défaut de finalisation rapide sur une tâche (fenêtre de la tâche → Valeurs par défaut pour finalisation rapide) — aucune tâche des objets choisis n'en a encore.",
"qr_url_mode": "Type de lien",
"qr_mode_companion": "Companion App",
"qr_mode_local": "Local (mDNS)",
"qr_mode_server": "URL serveur",
"overview": "Aperçu",
"strat_this_month": "Ce mois-ci",
"strat_later": "Plus tard",
"strat_week": "Semaine",
"strat_fortnight": "Quinzaine",
"strat_month": "Mois",
"strat_year": "Année",
"strat_other": "Autres",
"strat_status": "Statut",
"strat_empty_title": "Aucun objet de maintenance pour l'instant",
"strat_empty_content": "Ouvrez le panneau Maintenance pour ajouter votre premier objet — pompe de piscine, filtre de climatisation, véhicule, tout ce qui demande un entretien planifié.",
"strat_open_panel": "Ouvrir le panneau Maintenance",
"strat_add_object": "Ajouter un objet",
"strat_not_loaded": "**Maintenance Supporter** n'est pas chargé. Installez/activez d'abord l'intégration.",
"strat_group_by": "Regrouper les vues par",
"strat_group_area": "Par zone (par défaut)",
"strat_group_status": "Par statut (En retard / Déclenché / Bientôt dû / OK)",
"strat_group_floor": "Par étage (utilise les étages de HA)",
"strat_group_due_date": "Par échéance (En retard / Aujourd'hui / Cette semaine / Ce mois-ci / Plus tard)",
"strat_group_calendar": "Calendrier glissant (Semaine / Quinzaine / Mois / Année)",
"strat_editor_help": "La vue « Aperçu » est toujours la première. Les groupes vides sont ignorés.",
"picker_name_calendar": "Maintenance Supporter — Calendrier",
"picker_name_panel": "Maintenance Supporter — Panneau en carte",
"picker_name_budget": "Maintenance Supporter — Budget de maintenance",
"picker_name_groups": "Maintenance Supporter — Groupes",
"picker_name_vacation": "Maintenance Supporter — Mode vacances",
"picker_name_battery_fleet": "Parc de piles",
"picker_name_section": "Maintenance Supporter — Section de tâches",
"picker_name_vacation_status": "Maintenance Supporter — État du mode vacances",
"picker_name_budget_status": "Maintenance Supporter — État du budget",
"picker_desc_card": "Aperçu de vos tâches de maintenance avec actions rapides.",
"picker_desc_calendar": "Calendrier glissant des tâches de maintenance avec fenêtres de 7/14/30/365 jours, icônes de source et pastilles de confiance des prévisions.",
"picker_desc_panel": "Le panneau Maintenance Supporter complet sous forme de carte — pour une vue panneau / sous-vue de tableau de bord sans entrée dans la barre latérale.",
"picker_desc_budget": "Modifiez le budget mensuel et annuel directement sur le tableau de bord.",
"picker_desc_groups": "Créez, renommez et supprimez des groupes de tâches directement sur le tableau de bord.",
"picker_desc_vacation": "Activez ou désactivez le mode vacances et définissez ses dates directement sur le tableau de bord.",
"picker_desc_battery_fleet": "Toutes les piles suivies : ce qui est faible maintenant, ce qui sera bientôt vide et ce qu'il faut acheter.",
"picker_desc_dashboard": "Tableau de bord généré automatiquement. Regroupez les vues par zone, statut, étage ou échéance — au choix dans l'éditeur de stratégie ou en YAML.",
"picker_desc_section": "Intégrez les tâches de maintenance (filtrables par zone, statut, échéance) comme section dans n'importe quelle vue du tableau de bord.",
"picker_desc_vacation_status": "Bannière compacte d'état du mode vacances. Touchez pour ouvrir les réglages dans le panneau.",
"picker_desc_budget_status": "Aperçu du budget de maintenance mensuel et annuel. Touchez pour les détails.",
"picker_desc_groups_status": "Liste des groupes de tâches configurés avec le nombre de tâches de chacun.",
"hist_note_button": "Terminé via un bouton du tableau de bord",
"hist_note_todo": "Terminé depuis la liste de tâches",
"hist_note_voice": "Terminé par la voix",
"hist_note_notification": "Terminé depuis la notification",
"hist_note_nfc": "Terminé via un tag NFC",
"hist_note_shopping_list": "Terminé depuis la liste de courses",
"hist_note_todo_mirror": "Terminé depuis une liste de tâches reflétée",
"hist_note_skipped_notification": "Ignoré depuis la notification",
"hist_note_skipped_button": "Ignoré via un bouton du tableau de bord",
"hist_note_sensor_triggered": "Déclencheur de capteur activé",
"hist_note_initial": "Valeur initiale définie lors de la création de la tâche",
"hist_note_auto_recovered": "Terminé automatiquement : capteur rétabli ({value})",
"hist_note_reset": "Réinitialisé au {date}",
"hist_note_trigger_replaced": "Entité déclencheuse remplacée : {old} → {new}",
"hist_note_trigger_removed": "Déclencheur de capteur supprimé (entité : {entity}). Planning converti en {schedule}.",
"hist_note_compound_removed": "Entité {entity} retirée du déclencheur composé ; conditions restantes : {count}.",
"recent_activities": "Activités récentes",
"search_notes": "Rechercher dans les notes",
"avg_cost": "Ø Coût",
@@ -716,6 +795,7 @@
"groups_manage_tasks": "Gérer les affectations de tâches…",
"groups_open_panel": "Ouvrir dans le panneau",
"on_complete_action_target_hint": "Note : le domaine de l'entité doit correspondre au service — p. ex. 'button.press' ne fonctionne que sur button.*, 'counter.increment' seulement sur counter.*, 'input_button.press' seulement sur input_button.* etc. En cas de non-correspondance, l'action échoue silencieusement (HA journalise 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Pas quand la tâche se termine d'elle-même (son capteur s'est rétabli)",
"show_all_objects": "Afficher tous les objets",
"show_all_tasks": "Effacer le filtre — afficher toutes les tâches",
"filter_to_overdue": "Filtrer la liste sur les tâches en retard uniquement",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "« {name} » : tâches {before} → {after}",
"adopt_problem_adopt": "Adopter la sélection",
"adopt_problem_done": "{tasks} capteur(s) de problème adopté(s)",
"adopt_problem_done_one": "1 capteur de problème adopté",
"views_label": "Vues",
"views_none": "— Aucune vue —",
"views_manage": "Enregistrer / gérer les vues",
@@ -1013,6 +1094,7 @@
"setups_none": "Aucun appareil pris en charge avec des capteurs de consommables non reliés.",
"setups_adopt": "Configurer la sélection",
"setups_done": "{tasks} tâches reliées aux capteurs créées.",
"setups_done_one": "1 tâche reliée aux capteurs créée.",
"complete_parts_used": "Pièces utilisées cette fois",
"part_delete_confirm": "Supprimer la pièce '{name}' ? Son suivi de stock, ses liens aux tâches et tout rappel d'achat ouvert seront supprimés.",
"baseline_start_value": "Valeur de départ (facultatif)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "même modèle sur un autre appareil",
"setups_maybe_covered": "Existe probablement déjà sous « {name} »",
"setups_already": "Déjà présent : {names}",
"setups_reset_line": "La terminer réinitialise aussi le compteur dans l'intégration (« {button} »)",
"setups_reset_enable": "— le bouton sera activé pour cela",
"reset_offers_title": "Réinitialiser les compteurs à la finalisation",
"reset_offers_hint": "Ces tâches surveillent un compteur que l'intégration tient elle-même. Une fois la liaison faite, terminer la tâche ici appuie aussi sur le bouton de réinitialisation de l'intégration — sinon le compteur continue de tourner et la tâche redevient aussitôt due.",
"reset_offers_before": "Maintenant : la terminer laisse tourner le compteur dans {integration}",
"reset_offers_after": "Ensuite : appuie aussi sur « {button} »",
"reset_offers_renamed": "Renommée depuis sa configuration — vérifiez que cette tâche concerne bien cette pièce",
"reset_offers_apply": "Relier les réinitialisations",
"reset_offers_done": "{count} tâches réinitialisent désormais leur compteur à la finalisation",
"reset_offers_done_one": "1 tâche réinitialise désormais son compteur à la finalisation",
"setups_before_after": "Tâches de l'objet : {before} → {after}",
"setups_new_object_count": "Nouvel objet « {name} » · tâches : {count}",
"schedule_preview_title": "Prochaines échéances",
@@ -1046,6 +1138,7 @@
"history_auto": "Automatique",
"battery_fleet_title": "Parc de piles",
"battery_fleet_none_low": "Toutes les piles OK — rien à remplacer.",
"battery_fleet_progress": "{n} à remplacer",
"battery_fleet_buy_now": "Acheter",
"battery_fleet_soon": "Bientôt nécessaire",
"battery_fleet_soon_hint": "Estimé d'après le dernier remplacement — commandez à l'avance.",
@@ -1099,8 +1192,12 @@
"dismiss": "Ignorer",
"gs_label": "Premiers pas — ces conseils disparaissent à mesure que votre configuration grandit",
"gs_setups_chip": "Configurations suggérées : {n} appareils avec déclencheurs pré-câblés trouvés",
"gs_setups_chip_one": "Configurations suggérées : 1 appareil avec déclencheurs pré-câblés trouvé",
"gs_adopt_chip": "{n} capteurs de problème peuvent devenir des tâches de maintenance",
"gs_adopt_chip_one": "1 capteur de problème peut devenir une tâche de maintenance",
"gs_fleet_chip": "Un clic configure le parc de piles",
"gs_reset_chip": "{n} tâches peuvent réinitialiser leur compteur dans l'intégration",
"gs_reset_chip_one": "1 tâche peut réinitialiser son compteur dans l'intégration",
"cal_editor_window": "Fenêtre par défaut",
"cal_editor_window_week": "Semaine (7 jours)",
"cal_editor_window_fortnight": "Quinzaine (14 jours)",
@@ -243,6 +243,26 @@
"sort_task_name": "कार्य का नाम",
"all_objects": "सभी वस्तुएँ",
"all_parts": "सभी पुर्ज़े",
"all_areas": "सभी क्षेत्र",
"areas_filter": "क्षेत्र फ़िल्टर करें…",
"areas_filter_none": "फ़िल्टर से कोई क्षेत्र मेल नहीं खाता।",
"area_cost_year": "{year} की लागत",
"area_object_count": "{n} वस्तुएँ",
"area_object_count_one": "1 वस्तु",
"area_range_12m": "पिछले 12 महीने",
"area_range_all": "पूरी अवधि",
"area_task_filter": "कार्य के अनुसार फ़िल्टर करें…",
"area_kpi_completions": "पूर्णताएँ",
"area_kpi_total_time": "कुल समय",
"area_cost_per_month": "प्रति माह लागत",
"area_cost_per_year": "प्रति वर्ष लागत",
"area_cost_per_object": "प्रति वस्तु लागत",
"area_cost_share": "हिस्सा",
"area_history_section": "इतिहास (सभी वस्तुएँ)",
"area_report_print": "क्षेत्र रिपोर्ट (PDF)",
"area_report_title": "क्षेत्र रिपोर्ट",
"area_report_period": "अवधि",
"area_no_costs": "इस अवधि में कोई लागत दर्ज नहीं है।",
"no_tasks_yet": "अभी कोई कार्य नहीं",
"add_first_task": "पहला कार्य जोड़ें",
"trigger_configuration": "ट्रिगर कॉन्फ़िगरेशन",
@@ -401,11 +421,70 @@
"qr_download": "SVG डाउनलोड करें",
"qr_action_view": "रखरखाव जानकारी देखें",
"qr_action_complete": "रखरखाव को पूर्ण के रूप में चिह्नित करें",
"qr_action_quick_complete": "त्वरित-पूर्ण — बिना संवाद के",
"qr_print_quick_none": "त्वरित-पूर्ण कोड के लिए किसी कार्य पर त्वरित-पूर्ण डिफ़ॉल्ट चाहिए (कार्य संवाद → त्वरित-पूर्ण डिफ़ॉल्ट) — चुनी गई वस्तुओं के किसी कार्य पर अभी ये नहीं हैं।",
"qr_url_mode": "लिंक प्रकार",
"qr_mode_companion": "Companion ऐप",
"qr_mode_local": "लोकल (mDNS)",
"qr_mode_server": "सर्वर URL",
"overview": "अवलोकन",
"strat_this_month": "इस महीने",
"strat_later": "बाद में",
"strat_week": "सप्ताह",
"strat_fortnight": "पखवाड़ा",
"strat_month": "महीना",
"strat_year": "वर्ष",
"strat_other": "अन्य",
"strat_status": "स्थिति",
"strat_empty_title": "अभी कोई रखरखाव वस्तु नहीं",
"strat_empty_content": "अपनी पहली वस्तु जोड़ने के लिए रखरखाव पैनल खोलें — पूल पंप, HVAC फ़िल्टर, वाहन, कुछ भी जिसे नियमित देखभाल की ज़रूरत हो।",
"strat_open_panel": "रखरखाव पैनल खोलें",
"strat_add_object": "वस्तु जोड़ें",
"strat_not_loaded": "**Maintenance Supporter** लोड नहीं है। पहले इंटीग्रेशन इंस्टॉल/सक्षम करें।",
"strat_group_by": "व्यू को इसके अनुसार समूहित करें",
"strat_group_area": "क्षेत्र के अनुसार (डिफ़ॉल्ट)",
"strat_group_status": "स्थिति के अनुसार (अतिदेय / ट्रिगर हुआ / जल्द देय / ठीक)",
"strat_group_floor": "मंज़िल के अनुसार (HA की मंज़िलों का उपयोग)",
"strat_group_due_date": "देय तिथि के अनुसार (अतिदेय / आज / इस सप्ताह / इस महीने / बाद में)",
"strat_group_calendar": "रोलिंग कैलेंडर (सप्ताह / पखवाड़ा / महीना / वर्ष)",
"strat_editor_help": "\"अवलोकन\" व्यू हमेशा पहले आता है। खाली समूह छोड़ दिए जाते हैं।",
"picker_name_calendar": "Maintenance Supporter — कैलेंडर",
"picker_name_panel": "Maintenance Supporter — पैनल कार्ड",
"picker_name_budget": "Maintenance Supporter — रखरखाव बजट",
"picker_name_groups": "Maintenance Supporter — समूह",
"picker_name_vacation": "Maintenance Supporter — अवकाश मोड",
"picker_name_battery_fleet": "बैटरी फ्लीट",
"picker_name_section": "Maintenance Supporter — सेक्शन",
"picker_name_vacation_status": "Maintenance Supporter — अवकाश स्थिति",
"picker_name_budget_status": "Maintenance Supporter — बजट स्थिति",
"picker_desc_card": "त्वरित क्रियाओं के साथ आपके रखरखाव कार्यों का अवलोकन।",
"picker_desc_calendar": "7/14/30/365 दिन की विंडो, स्रोत आइकन और पूर्वानुमान-विश्वास बैज के साथ रखरखाव कार्यों का रोलिंग कैलेंडर।",
"picker_desc_panel": "पूरा Maintenance Supporter पैनल एक कार्ड के रूप में — साइडबार प्रविष्टि के बिना पैनल व्यू / डैशबोर्ड सबव्यू के लिए।",
"picker_desc_budget": "मासिक और वार्षिक बजट सीधे डैशबोर्ड पर संपादित करें।",
"picker_desc_groups": "डैशबोर्ड पर ही कार्य समूह बनाएँ, नाम बदलें और हटाएँ।",
"picker_desc_vacation": "डैशबोर्ड पर ही अवकाश मोड चालू या बंद करें और उसकी तिथियाँ सेट करें।",
"picker_desc_battery_fleet": "सभी ट्रैक की गई बैटरियाँ: अभी कौन-सी कम हैं, कौन-सी जल्द खत्म होंगी और क्या खरीदना है।",
"picker_desc_dashboard": "स्वतः बनाया गया डैशबोर्ड। व्यू को क्षेत्र, स्थिति, मंज़िल या देय तिथि के अनुसार समूहित करें — स्ट्रैटेजी एडिटर या YAML से चुनें।",
"picker_desc_section": "रखरखाव कार्यों (क्षेत्र, स्थिति, देय तिथि से फ़िल्टर योग्य) को किसी भी डैशबोर्ड व्यू में सेक्शन के रूप में जोड़ें।",
"picker_desc_vacation_status": "अवकाश मोड की स्थिति का संक्षिप्त बैनर। पैनल में सेटिंग्स खोलने के लिए टैप करें।",
"picker_desc_budget_status": "मासिक और वार्षिक रखरखाव बजट का अवलोकन। विवरण के लिए टैप करें।",
"picker_desc_groups_status": "कॉन्फ़िगर किए गए कार्य समूहों की सूची, प्रत्येक में कार्यों की संख्या के साथ।",
"hist_note_button": "डैशबोर्ड बटन से पूर्ण किया गया",
"hist_note_todo": "टू-डू सूची से पूर्ण किया गया",
"hist_note_voice": "आवाज़ से पूर्ण किया गया",
"hist_note_notification": "सूचना से पूर्ण किया गया",
"hist_note_nfc": "NFC टैग से पूर्ण किया गया",
"hist_note_shopping_list": "शॉपिंग सूची से पूर्ण किया गया",
"hist_note_todo_mirror": "मिरर की गई टू-डू सूची से पूर्ण किया गया",
"hist_note_skipped_notification": "सूचना से छोड़ा गया",
"hist_note_skipped_button": "डैशबोर्ड बटन से छोड़ा गया",
"hist_note_sensor_triggered": "सेंसर ट्रिगर सक्रिय हुआ",
"hist_note_initial": "कार्य बनाते समय प्रारंभिक मान सेट किया गया",
"hist_note_auto_recovered": "स्वतः पूर्ण: सेंसर ठीक हुआ ({value})",
"hist_note_reset": "{date} पर रीसेट किया गया",
"hist_note_trigger_replaced": "ट्रिगर एंटिटी बदली गई: {old} → {new}",
"hist_note_trigger_removed": "सेंसर ट्रिगर हटाया गया (एंटिटी थी: {entity})। शेड्यूल {schedule} में बदला गया।",
"hist_note_compound_removed": "एंटिटी {entity} को संयुक्त ट्रिगर से हटाया गया; शेष शर्तें: {count}।",
"recent_activities": "हाल की गतिविधियाँ",
"search_notes": "टिप्पणियाँ खोजें",
"avg_cost": "औसत लागत",
@@ -703,6 +782,7 @@
"on_complete_action_desc": "कार्य पूर्ण होने पर एक HA सेवा कॉल करता है — उदा. डिवाइस पर काउंटर रीसेट करना।",
"on_complete_action_target": "लक्ष्य एंटिटी",
"on_complete_action_target_hint": "ध्यान दें: एंटिटी डोमेन सेवा से मेल खाना चाहिए — उदा. 'button.press' केवल button.* पर काम करता है, 'counter.increment' केवल counter.* पर, 'input_button.press' केवल input_button.* पर आदि। मेल न खाने पर क्रिया चुपचाप विफल हो जाएगी (HA लॉग करता है 'Referenced entities ... missing or not currently available')।",
"on_complete_action_skip_auto": "तब नहीं जब कार्य स्वयं पूर्ण हो (उसका सेंसर ठीक हो गया)",
"on_complete_action_data": "डेटा (JSON, वैकल्पिक)",
"on_complete_action_test": "कॉन्फ़िगरेशन सत्यापित करें",
"on_complete_action_test_success": "✓ कॉन्फ़िगरेशन वैध (क्रिया केवल कार्य पूर्ण होने पर चलेगी)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "\"{name}\": कार्य {before} → {after}",
"adopt_problem_adopt": "चयनित अपनाएँ",
"adopt_problem_done": "{tasks} समस्या सेंसर अपनाए गए",
"adopt_problem_done_one": "1 समस्या सेंसर अपनाया गया",
"views_label": "व्यू",
"views_none": "— कोई व्यू नहीं —",
"views_manage": "व्यू सहेजें / प्रबंधित करें",
@@ -1013,6 +1094,7 @@
"setups_none": "बिना जुड़े उपभोग्य-सामग्री सेंसर वाले समर्थित उपकरण नहीं मिले।",
"setups_adopt": "चयनित सेट करें",
"setups_done": "{tasks} सेंसर-जुड़े कार्य बनाए गए।",
"setups_done_one": "1 सेंसर-जुड़ा कार्य बनाया गया।",
"complete_parts_used": "इस बार उपयोग किए गए पुर्ज़े",
"part_delete_confirm": "पुर्ज़ा '{name}' हटाएँ? इसका स्टॉक ट्रैकिंग, कार्य लिंक और कोई खुला खरीद-अनुस्मारक हट जाएगा।",
"baseline_start_value": "प्रारंभिक मान (वैकल्पिक)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "दूसरे डिवाइस पर वही मॉडल",
"setups_maybe_covered": "शायद पहले से \"{name}\" के रूप में मौजूद है",
"setups_already": "पहले से मौजूद: {names}",
"setups_reset_line": "पूर्ण करने पर इंटीग्रेशन में काउंटर भी रीसेट होता है (“{button}”)",
"setups_reset_enable": "— इसके लिए बटन चालू किया जाएगा",
"reset_offers_title": "पूर्ण होने पर काउंटर रीसेट करें",
"reset_offers_hint": "ये कार्य एक ऐसे काउंटर पर नज़र रखते हैं जिसे इंटीग्रेशन खुद रखता है। जोड़ने पर, यहाँ कार्य पूर्ण करने से इंटीग्रेशन का रीसेट बटन भी दबता है — वरना काउंटर चलता रहता है और कार्य तुरंत फिर से देय हो जाता है।",
"reset_offers_before": "अभी: पूर्ण करने पर {integration} में काउंटर चलता रहता है",
"reset_offers_after": "फिर: “{button}” भी दबाया जाएगा",
"reset_offers_renamed": "सेटअप के बाद नाम बदला गया — जाँचें कि यह कार्य इसी पुर्ज़े के लिए है",
"reset_offers_apply": "रीसेट जोड़ें",
"reset_offers_done": "{count} कार्य अब पूर्ण होने पर अपना काउंटर रीसेट करते हैं",
"reset_offers_done_one": "1 कार्य अब पूर्ण होने पर अपना काउंटर रीसेट करता है",
"setups_before_after": "ऑब्जेक्ट के कार्य: {before} → {after}",
"setups_new_object_count": "नया ऑब्जेक्ट \"{name}\" · कार्य: {count}",
"schedule_preview_title": "अगली तिथियाँ",
@@ -1046,6 +1138,7 @@
"history_auto": "स्वचालित",
"battery_fleet_title": "बैटरी फ्लीट",
"battery_fleet_none_low": "सभी बैटरियाँ ठीक — बदलने को कुछ नहीं।",
"battery_fleet_progress": "बदलने हेतु: {n}",
"battery_fleet_buy_now": "अभी खरीदें",
"battery_fleet_soon": "जल्द आवश्यक",
"battery_fleet_soon_hint": "पिछले प्रतिस्थापन से अनुमानित — पहले ऑर्डर करें।",
@@ -1099,8 +1192,12 @@
"dismiss": "हटाएँ",
"gs_label": "शुरुआत — सेटअप बढ़ने पर ये संकेत हट जाते हैं",
"gs_setups_chip": "सुझाए गए सेटअप: पूर्व-निर्धारित ट्रिगर वाले {n} उपकरण मिले",
"gs_setups_chip_one": "सुझाए गए सेटअप: पूर्व-निर्धारित ट्रिगर वाला 1 उपकरण मिला",
"gs_adopt_chip": "{n} समस्या सेंसर रखरखाव कार्य बन सकते हैं",
"gs_adopt_chip_one": "1 समस्या सेंसर रखरखाव कार्य बन सकता है",
"gs_fleet_chip": "एक क्लिक में बैटरी बेड़ा सेट करें",
"gs_reset_chip": "{n} कार्य इंटीग्रेशन में अपना काउंटर रीसेट कर सकते हैं",
"gs_reset_chip_one": "1 कार्य इंटीग्रेशन में अपना काउंटर रीसेट कर सकता है",
"cal_editor_window": "डिफ़ॉल्ट विंडो",
"cal_editor_window_week": "सप्ताह (7 दिन)",
"cal_editor_window_fortnight": "पखवाड़ा (14 दिन)",
@@ -243,6 +243,26 @@
"sort_task_name": "Feladat neve",
"all_objects": "Minden objektum",
"all_parts": "Összes alkatrész",
"all_areas": "Összes terület",
"areas_filter": "Területek szűrése…",
"areas_filter_none": "Egy terület sem felel meg a szűrőnek.",
"area_cost_year": "Költség {year}",
"area_object_count": "{n} objektum",
"area_object_count_one": "1 objektum",
"area_range_12m": "Elmúlt 12 hónap",
"area_range_all": "Teljes időszak",
"area_task_filter": "Szűrés feladat szerint…",
"area_kpi_completions": "Elvégzések",
"area_kpi_total_time": "Teljes idő",
"area_cost_per_month": "Költség havonta",
"area_cost_per_year": "Költség évente",
"area_cost_per_object": "Költség objektumonként",
"area_cost_share": "Arány",
"area_history_section": "Előzmények (összes objektum)",
"area_report_print": "Területi jelentés (PDF)",
"area_report_title": "Területi jelentés",
"area_report_period": "Időszak",
"area_no_costs": "Ebben az időszakban nincs rögzített költség.",
"no_tasks_yet": "Még nincs feladat",
"add_first_task": "Első feladat hozzáadása",
"trigger_configuration": "Kiváltó beállítása",
@@ -401,11 +421,70 @@
"qr_download": "SVG letöltése",
"qr_action_view": "Karbantartási adatok megtekintése",
"qr_action_complete": "Karbantartás elvégzettként jelölése",
"qr_action_quick_complete": "Gyorselvégzés — párbeszédablak nélkül",
"qr_print_quick_none": "A gyorselvégzés-kódokhoz a feladaton gyorselvégzés-alapértékek kellenek (feladat párbeszédablaka → Gyorselvégzés alapértékei) — a kiválasztott objektumok egyik feladatán sincsenek még.",
"qr_url_mode": "Hivatkozás típusa",
"qr_mode_companion": "Companion alkalmazás",
"qr_mode_local": "Helyi (mDNS)",
"qr_mode_server": "Szerver URL",
"overview": "Áttekintés",
"strat_this_month": "Ebben a hónapban",
"strat_later": "Később",
"strat_week": "Hét",
"strat_fortnight": "Két hét",
"strat_month": "Hónap",
"strat_year": "Év",
"strat_other": "Egyéb",
"strat_status": "Állapot",
"strat_empty_title": "Még nincsenek karbantartási objektumok",
"strat_empty_content": "Nyissa meg a Karbantartás panelt az első objektum hozzáadásához — medenceszivattyú, légtechnikai szűrő, jármű, bármi, ami ütemezett gondozást igényel.",
"strat_open_panel": "Karbantartás panel megnyitása",
"strat_add_object": "Objektum hozzáadása",
"strat_not_loaded": "A **Maintenance Supporter** nincs betöltve. Először telepítse/engedélyezze az integrációt.",
"strat_group_by": "Nézetek csoportosítása",
"strat_group_area": "Terület szerint (alapértelmezett)",
"strat_group_status": "Állapot szerint (Lejárt / Aktiválva / Hamarosan esedékes / OK)",
"strat_group_floor": "Szint szerint (a HA szintjeit használja)",
"strat_group_due_date": "Határidő szerint (Lejárt / Ma / Ezen a héten / Ebben a hónapban / Később)",
"strat_group_calendar": "Gördülő naptár (Hét / Két hét / Hónap / Év)",
"strat_editor_help": "Az „Áttekintés” nézet mindig az első. Az üres csoportok kimaradnak.",
"picker_name_calendar": "Maintenance Supporter — Naptár",
"picker_name_panel": "Maintenance Supporter — Panel kártyaként",
"picker_name_budget": "Maintenance Supporter — Karbantartási költségkeret",
"picker_name_groups": "Maintenance Supporter — Csoportok",
"picker_name_vacation": "Maintenance Supporter — Nyaralási mód",
"picker_name_battery_fleet": "Elemflotta",
"picker_name_section": "Maintenance Supporter — Szakasz",
"picker_name_vacation_status": "Maintenance Supporter — Nyaralás állapota",
"picker_name_budget_status": "Maintenance Supporter — Költségkeret állapota",
"picker_desc_card": "Karbantartási feladatainak áttekintése gyorsműveletekkel.",
"picker_desc_calendar": "A karbantartási feladatok gördülő naptára 7/14/30/365 napos időablakokkal, forrásikonokkal és előrejelzési megbízhatósági címkékkel.",
"picker_desc_panel": "A teljes Maintenance Supporter panel kártyaként — panelnézethez / irányítópult-alnézethez oldalsáv-bejegyzés nélkül.",
"picker_desc_budget": "A havi és éves költségkeret szerkesztése közvetlenül az irányítópulton.",
"picker_desc_groups": "Feladatcsoportok létrehozása, átnevezése és törlése közvetlenül az irányítópulton.",
"picker_desc_vacation": "A nyaralási mód be- és kikapcsolása, valamint dátumainak beállítása közvetlenül az irányítópulton.",
"picker_desc_battery_fleet": "Minden nyomon követett elem: mi merült le most, mi fogy ki hamarosan, és mit kell venni.",
"picker_desc_dashboard": "Automatikusan létrehozott irányítópult. A nézetek terület, állapot, szint vagy határidő szerint csoportosíthatók — a stratégiaszerkesztőben vagy YAML-ben választható.",
"picker_desc_section": "Karbantartási feladatok (terület, állapot, határidő szerint szűrhetők) beágyazása szakaszként bármely irányítópult-nézetbe.",
"picker_desc_vacation_status": "Kompakt állapotsáv a nyaralási módhoz. Koppintson a beállítások megnyitásához a panelen.",
"picker_desc_budget_status": "A havi és éves karbantartási költségkeret áttekintése. Koppintson a részletekért.",
"picker_desc_groups_status": "A beállított feladatcsoportok listája a bennük lévő feladatok számával.",
"hist_note_button": "Elvégezve az irányítópult gombjával",
"hist_note_todo": "Elvégezve a teendőlistából",
"hist_note_voice": "Elvégezve hangvezérléssel",
"hist_note_notification": "Elvégezve az értesítésből",
"hist_note_nfc": "Elvégezve NFC címkével",
"hist_note_shopping_list": "Elvégezve a bevásárlólistából",
"hist_note_todo_mirror": "Elvégezve egy tükrözött teendőlistából",
"hist_note_skipped_notification": "Kihagyva az értesítésből",
"hist_note_skipped_button": "Kihagyva az irányítópult gombjával",
"hist_note_sensor_triggered": "Érzékelős kiváltó aktiválva",
"hist_note_initial": "Kezdőérték a feladat létrehozásakor beállítva",
"hist_note_auto_recovered": "Automatikusan elvégezve: az érzékelő helyreállt ({value})",
"hist_note_reset": "Visszaállítva erre: {date}",
"hist_note_trigger_replaced": "Kiváltó entitás lecserélve: {old} → {new}",
"hist_note_trigger_removed": "Érzékelős kiváltó eltávolítva (entitás: {entity}). Ütemezés átállítva: {schedule}.",
"hist_note_compound_removed": "{entity} entitás eltávolítva az összetett kiváltóból; hátralévő feltételek: {count}.",
"recent_activities": "Legutóbbi tevékenységek",
"search_notes": "Keresés a megjegyzésekben",
"avg_cost": "Átl. költség",
@@ -709,6 +788,7 @@
"on_complete_action_desc": "HA szolgáltatást hív meg a feladat elvégzésekor — pl. számláló nullázása az eszközön.",
"on_complete_action_target": "Cél entitás",
"on_complete_action_target_hint": "Megjegyzés: az entitás doménjének egyeznie kell a szolgáltatással — pl. a „button.press” csak button.* entitáson működik, a „counter.increment” csak counter.* entitáson, az „input_button.press” csak input_button.* entitáson stb. Eltérés esetén a művelet csendben meghiúsul (a HA naplóban: „Referenced entities ... missing or not currently available”).",
"on_complete_action_skip_auto": "Nem, ha a feladat magától elvégződik (az érzékelője helyreállt)",
"on_complete_action_data": "Adatok (JSON, opcionális)",
"on_complete_action_test": "Konfiguráció ellenőrzése",
"on_complete_action_test_success": "✓ A konfiguráció érvényes (a művelet csak a feladat elvégzésekor fut le)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "„{name}”: feladatok {before} → {after}",
"adopt_problem_adopt": "Kijelöltek átvétele",
"adopt_problem_done": "{tasks} problémaérzékelő átvéve",
"adopt_problem_done_one": "1 problémaérzékelő átvéve",
"views_label": "Nézetek",
"views_none": "— Nincs nézet —",
"views_manage": "Nézetek mentése / kezelése",
@@ -1013,6 +1094,7 @@
"setups_none": "Nem található támogatott eszköz bekötetlen fogyóeszköz-érzékelővel.",
"setups_adopt": "Kijelöltek beállítása",
"setups_done": "{tasks} érzékelőhöz kötött feladat létrehozva.",
"setups_done_one": "1 érzékelőhöz kötött feladat létrehozva.",
"complete_parts_used": "Most felhasznált alkatrészek",
"part_delete_confirm": "Törli a(z) „{name}” alkatrészt? A készletkövetése, a feladatkapcsolatai és minden nyitott vásárlási emlékeztetője is törlődik.",
"baseline_start_value": "Kezdő leolvasás (opcionális)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "ugyanaz a típus egy másik eszközön",
"setups_maybe_covered": "Valószínűleg már megvan „{name}” néven",
"setups_already": "Már megvan: {names}",
"setups_reset_line": "Az elvégzés az integráció számlálóját is nullázza („{button}”)",
"setups_reset_enable": "— ehhez a gomb be lesz kapcsolva",
"reset_offers_title": "Számlálók nullázása elvégzéskor",
"reset_offers_hint": "Ezek a feladatok egy olyan számlálót figyelnek, amelyet maga az integráció vezet. Összekapcsolva a feladat itteni elvégzése az integráció nullázó gombját is megnyomja — különben a számláló tovább fut, és a feladat azonnal újra esedékessé válik.",
"reset_offers_before": "Most: elvégzéskor a(z) {integration} számlálója tovább fut",
"reset_offers_after": "Utána: ezt is megnyomja: „{button}”",
"reset_offers_renamed": "A beállítás óta átnevezték — ellenőrizze, hogy a feladat ehhez az alkatrészhez tartozik-e",
"reset_offers_apply": "Nullázások összekapcsolása",
"reset_offers_done": "{count} feladat mostantól elvégzéskor nullázza a számlálóját",
"reset_offers_done_one": "1 feladat mostantól elvégzéskor nullázza a számlálóját",
"setups_before_after": "Az objektum feladatai: {before} → {after}",
"setups_new_object_count": "Új objektum „{name}” · feladatok: {count}",
"schedule_preview_title": "Következő dátumok",
@@ -1046,6 +1138,7 @@
"history_auto": "Automatikus",
"battery_fleet_title": "Elemflotta",
"battery_fleet_none_low": "Minden elem rendben — nincs mit cserélni.",
"battery_fleet_progress": "Cserélendő: {n}",
"battery_fleet_buy_now": "Vásárlás most",
"battery_fleet_soon": "Hamarosan szükséges",
"battery_fleet_soon_hint": "Az utolsó cseredátumból előrejelezve — rendeljen előre.",
@@ -1099,8 +1192,12 @@
"dismiss": "Elrejtés",
"gs_label": "Első lépések — a tippek eltűnnek, ahogy a beállítás bővül",
"gs_setups_chip": "Javasolt beállítások: {n} eszköz előre bekötött triggerekkel",
"gs_setups_chip_one": "Javasolt beállítások: 1 eszköz előre bekötött triggerekkel",
"gs_adopt_chip": "{n} problémaérzékelő karbantartási feladattá válhat",
"gs_adopt_chip_one": "1 problémaérzékelő karbantartási feladattá válhat",
"gs_fleet_chip": "Egy kattintás beállítja az elemflottát",
"gs_reset_chip": "{n} feladat nullázhatja a számlálóját az integrációban",
"gs_reset_chip_one": "1 feladat nullázhatja a számlálóját az integrációban",
"cal_editor_window": "Alapértelmezett időablak",
"cal_editor_window_week": "Hét (7 nap)",
"cal_editor_window_fortnight": "Két hét (14 nap)",
@@ -241,6 +241,26 @@
"sort_task_name": "Nome attività",
"all_objects": "Tutti gli oggetti",
"all_parts": "Tutti i ricambi",
"all_areas": "Tutte le aree",
"areas_filter": "Filtra aree…",
"areas_filter_none": "Nessuna area corrisponde al filtro.",
"area_cost_year": "Costo {year}",
"area_object_count": "{n} oggetti",
"area_object_count_one": "1 oggetto",
"area_range_12m": "Ultimi 12 mesi",
"area_range_all": "Tutto il periodo",
"area_task_filter": "Filtra per attività…",
"area_kpi_completions": "Completamenti",
"area_kpi_total_time": "Tempo totale",
"area_cost_per_month": "Costo per mese",
"area_cost_per_year": "Costo per anno",
"area_cost_per_object": "Costo per oggetto",
"area_cost_share": "Quota",
"area_history_section": "Cronologia (tutti gli oggetti)",
"area_report_print": "Rapporto dell'area (PDF)",
"area_report_title": "Rapporto dell'area",
"area_report_period": "Periodo",
"area_no_costs": "Nessun costo registrato in questo periodo.",
"no_tasks_yet": "Nessuna attività",
"add_first_task": "Aggiungi prima attività",
"trigger_configuration": "Configurazione trigger",
@@ -399,11 +419,70 @@
"qr_download": "Scarica SVG",
"qr_action_view": "Visualizza info manutenzione",
"qr_action_complete": "Segna manutenzione come completata",
"qr_action_quick_complete": "Completamento rapido — senza finestra",
"qr_print_quick_none": "I codici di completamento rapido richiedono valori predefiniti di completamento rapido su un'attività (finestra dell'attività → Valori predefiniti completamento rapido) — nessuna attività degli oggetti scelti li ha ancora.",
"qr_url_mode": "Tipo di link",
"qr_mode_companion": "Companion App",
"qr_mode_local": "Locale (mDNS)",
"qr_mode_server": "URL server",
"overview": "Panoramica",
"strat_this_month": "Questo mese",
"strat_later": "Più avanti",
"strat_week": "Settimana",
"strat_fortnight": "Due settimane",
"strat_month": "Mese",
"strat_year": "Anno",
"strat_other": "Altro",
"strat_status": "Stato",
"strat_empty_title": "Nessun oggetto di manutenzione",
"strat_empty_content": "Apri il pannello Manutenzione per aggiungere il tuo primo oggetto — pompa della piscina, filtro del climatizzatore, veicolo, qualsiasi cosa richieda una manutenzione programmata.",
"strat_open_panel": "Apri il pannello Manutenzione",
"strat_add_object": "Aggiungi oggetto",
"strat_not_loaded": "**Maintenance Supporter** non è caricato. Installa/attiva prima l'integrazione.",
"strat_group_by": "Raggruppa le viste per",
"strat_group_area": "Per area (predefinito)",
"strat_group_status": "Per stato (Scaduto / Attivato / In scadenza / OK)",
"strat_group_floor": "Per piano (usa i piani di HA)",
"strat_group_due_date": "Per scadenza (Scaduto / Oggi / Questa settimana / Questo mese / Più avanti)",
"strat_group_calendar": "Calendario mobile (Settimana / Due settimane / Mese / Anno)",
"strat_editor_help": "La vista \"Panoramica\" è sempre la prima. I gruppi vuoti vengono saltati.",
"picker_name_calendar": "Maintenance Supporter — Calendario",
"picker_name_panel": "Maintenance Supporter — Pannello come scheda",
"picker_name_budget": "Maintenance Supporter — Budget di manutenzione",
"picker_name_groups": "Maintenance Supporter — Gruppi",
"picker_name_vacation": "Maintenance Supporter — Modalità vacanza",
"picker_name_battery_fleet": "Parco batterie",
"picker_name_section": "Maintenance Supporter — Sezione",
"picker_name_vacation_status": "Maintenance Supporter — Stato vacanza",
"picker_name_budget_status": "Maintenance Supporter — Stato del budget",
"picker_desc_card": "Panoramica delle tue attività di manutenzione con azioni rapide.",
"picker_desc_calendar": "Calendario mobile delle attività di manutenzione con finestre di 7/14/30/365 giorni, icone della fonte ed etichette di confidenza della previsione.",
"picker_desc_panel": "L'intero pannello di Maintenance Supporter come scheda — per una vista pannello / sottovista della dashboard senza voce nella barra laterale.",
"picker_desc_budget": "Modifica il budget mensile e annuale direttamente nella dashboard.",
"picker_desc_groups": "Crea, rinomina ed elimina gruppi di attività direttamente nella dashboard.",
"picker_desc_vacation": "Attiva o disattiva la modalità vacanza e impostane le date direttamente nella dashboard.",
"picker_desc_battery_fleet": "Tutte le batterie monitorate: quali sono scariche ora, quali si esauriranno presto e cosa comprare.",
"picker_desc_dashboard": "Dashboard generata automaticamente. Raggruppa le viste per area, stato, piano o scadenza — da scegliere nell'editor della strategia o in YAML.",
"picker_desc_section": "Incorpora le attività di manutenzione (filtrabili per area, stato, scadenza) come sezione in qualsiasi vista della dashboard.",
"picker_desc_vacation_status": "Banner compatto dello stato della modalità vacanza. Tocca per aprire le impostazioni nel pannello.",
"picker_desc_budget_status": "Panoramica del budget di manutenzione mensile e annuale. Tocca per i dettagli.",
"picker_desc_groups_status": "Elenco dei gruppi di attività configurati con il numero di attività di ciascuno.",
"hist_note_button": "Completato dal pulsante della dashboard",
"hist_note_todo": "Completato dalla lista di cose da fare",
"hist_note_voice": "Completato con la voce",
"hist_note_notification": "Completato dalla notifica",
"hist_note_nfc": "Completato tramite tag NFC",
"hist_note_shopping_list": "Completato dalla lista della spesa",
"hist_note_todo_mirror": "Completato da una lista di cose da fare rispecchiata",
"hist_note_skipped_notification": "Saltato dalla notifica",
"hist_note_skipped_button": "Saltato dal pulsante della dashboard",
"hist_note_sensor_triggered": "Trigger del sensore attivato",
"hist_note_initial": "Valore iniziale impostato alla creazione dell'attività",
"hist_note_auto_recovered": "Completato automaticamente: sensore ripristinato ({value})",
"hist_note_reset": "Reimpostato al {date}",
"hist_note_trigger_replaced": "Entità trigger sostituita: {old} → {new}",
"hist_note_trigger_removed": "Trigger del sensore rimosso (entità: {entity}). Pianificazione convertita in {schedule}.",
"hist_note_compound_removed": "Entità {entity} rimossa dal trigger composto; condizioni rimanenti: {count}.",
"recent_activities": "Attività recenti",
"search_notes": "Cerca nelle note",
"avg_cost": "Ø Costo",
@@ -716,6 +795,7 @@
"groups_manage_tasks": "Gestisci assegnazioni attività…",
"groups_open_panel": "Apri nel pannello",
"on_complete_action_target_hint": "Nota: il dominio dell'entità deve corrispondere al servizio — es. 'button.press' funziona solo su button.*, 'counter.increment' solo su counter.*, 'input_button.press' solo su input_button.* ecc. In caso di mancata corrispondenza l'azione fallisce silenziosamente (HA registra 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Non quando l'attività si completa da sola (il suo sensore si è ripristinato)",
"show_all_objects": "Mostra tutti gli oggetti",
"show_all_tasks": "Azzera filtro — mostra tutte le attività",
"filter_to_overdue": "Filtra l'elenco solo sulle attività in ritardo",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "«{name}»: attività {before} → {after}",
"adopt_problem_adopt": "Adotta selezionati",
"adopt_problem_done": "{tasks} sensore/i di problema adottato/i",
"adopt_problem_done_one": "1 sensore di problema adottato",
"views_label": "Viste",
"views_none": "— Nessuna vista —",
"views_manage": "Salva / gestisci viste",
@@ -1013,6 +1094,7 @@
"setups_none": "Nessun dispositivo supportato con sensori di consumabili non collegati.",
"setups_adopt": "Configura selezione",
"setups_done": "{tasks} attività collegate ai sensori create.",
"setups_done_one": "1 attività collegata ai sensori creata.",
"complete_parts_used": "Ricambi usati questa volta",
"part_delete_confirm": "Eliminare il ricambio '{name}'? Verranno rimossi il monitoraggio delle scorte, i collegamenti alle attività e ogni promemoria d'acquisto aperto.",
"baseline_start_value": "Valore iniziale (facoltativo)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "stesso modello su un altro dispositivo",
"setups_maybe_covered": "Probabilmente esiste già come «{name}»",
"setups_already": "Già presente: {names}",
"setups_reset_line": "Completarla azzera anche il contatore nell'integrazione («{button}»)",
"setups_reset_enable": "— il pulsante verrà attivato a questo scopo",
"reset_offers_title": "Azzera i contatori al completamento",
"reset_offers_hint": "Queste attività osservano un contatore gestito dall'integrazione stessa. Una volta collegate, completare l'attività qui preme anche il pulsante di azzeramento dell'integrazione — altrimenti il contatore continua a correre e l'attività torna subito in scadenza.",
"reset_offers_before": "Ora: completarla lascia correre il contatore in {integration}",
"reset_offers_after": "Poi: preme anche «{button}»",
"reset_offers_renamed": "Rinominata dopo la configurazione — verifica che questa attività riguardi questo componente",
"reset_offers_apply": "Collega gli azzeramenti",
"reset_offers_done": "{count} attività ora azzerano il contatore al completamento",
"reset_offers_done_one": "1 attività ora azzera il contatore al completamento",
"setups_before_after": "Attività dell'oggetto: {before} → {after}",
"setups_new_object_count": "Nuovo oggetto «{name}» · attività: {count}",
"schedule_preview_title": "Prossime date",
@@ -1046,6 +1138,7 @@
"history_auto": "Automatico",
"battery_fleet_title": "Parco batterie",
"battery_fleet_none_low": "Tutte le batterie OK — niente da sostituire.",
"battery_fleet_progress": "{n} da sostituire",
"battery_fleet_buy_now": "Da comprare",
"battery_fleet_soon": "Presto necessario",
"battery_fleet_soon_hint": "Stimato dall'ultima sostituzione — ordina in anticipo.",
@@ -1099,8 +1192,12 @@
"dismiss": "Ignora",
"gs_label": "Primi passi — questi suggerimenti scompaiono man mano che la configurazione cresce",
"gs_setups_chip": "Configurazioni suggerite: trovati {n} dispositivi con trigger preconfigurati",
"gs_setups_chip_one": "Configurazioni suggerite: trovato 1 dispositivo con trigger preconfigurati",
"gs_adopt_chip": "{n} sensori di problemi possono diventare attività di manutenzione",
"gs_adopt_chip_one": "1 sensore di problemi può diventare un'attività di manutenzione",
"gs_fleet_chip": "Un clic configura la flotta di batterie",
"gs_reset_chip": "{n} attività possono azzerare il contatore nell'integrazione",
"gs_reset_chip_one": "1 attività può azzerare il contatore nell'integrazione",
"cal_editor_window": "Finestra predefinita",
"cal_editor_window_week": "Settimana (7 giorni)",
"cal_editor_window_fortnight": "Due settimane (14 giorni)",
@@ -243,6 +243,26 @@
"sort_task_name": "タスク名",
"all_objects": "すべての対象",
"all_parts": "すべての部品",
"all_areas": "すべてのエリア",
"areas_filter": "エリアを絞り込む…",
"areas_filter_none": "条件に一致するエリアはありません。",
"area_cost_year": "{year}年の費用",
"area_object_count": "{n} 件の対象",
"area_object_count_one": "1 件の対象",
"area_range_12m": "過去12か月",
"area_range_all": "全期間",
"area_task_filter": "タスクで絞り込む…",
"area_kpi_completions": "完了回数",
"area_kpi_total_time": "合計所要時間",
"area_cost_per_month": "月別費用",
"area_cost_per_year": "年別費用",
"area_cost_per_object": "対象別費用",
"area_cost_share": "割合",
"area_history_section": "履歴(全対象)",
"area_report_print": "エリアレポート(PDF)",
"area_report_title": "エリアレポート",
"area_report_period": "期間",
"area_no_costs": "この期間に記録された費用はありません。",
"no_tasks_yet": "タスクがまだありません",
"add_first_task": "最初のタスクを追加",
"trigger_configuration": "トリガー設定",
@@ -401,11 +421,70 @@
"qr_download": "SVGをダウンロード",
"qr_action_view": "メンテナンス情報を表示",
"qr_action_complete": "メンテナンスを完了にする",
"qr_action_quick_complete": "クイック完了 — ダイアログなし",
"qr_print_quick_none": "クイック完了コードには、タスクのクイック完了の既定値が必要です(タスクのダイアログ → クイック完了の既定値)。選択したオブジェクトのタスクにはまだ設定されていません。",
"qr_url_mode": "リンク種別",
"qr_mode_companion": "コンパニオンアプリ",
"qr_mode_local": "ローカル(mDNS)",
"qr_mode_server": "サーバーURL",
"overview": "概要",
"strat_this_month": "今月",
"strat_later": "それ以降",
"strat_week": "1週間",
"strat_fortnight": "2週間",
"strat_month": "1か月",
"strat_year": "1年",
"strat_other": "その他",
"strat_status": "状態",
"strat_empty_title": "メンテナンス対象がまだありません",
"strat_empty_content": "メンテナンスパネルを開いて最初の対象を追加しましょう — プールポンプ、空調フィルター、車両など、定期的な手入れが必要なものなら何でも。",
"strat_open_panel": "メンテナンスパネルを開く",
"strat_add_object": "対象を追加",
"strat_not_loaded": "**Maintenance Supporter** が読み込まれていません。先にインテグレーションをインストール/有効化してください。",
"strat_group_by": "ビューのグループ化",
"strat_group_area": "エリア別(既定)",
"strat_group_status": "状態別(期限超過 / トリガー発生 / 期限間近 / OK)",
"strat_group_floor": "フロア別(HA のフロアを使用)",
"strat_group_due_date": "期限別(期限超過 / 今日 / 今週 / 今月 / それ以降)",
"strat_group_calendar": "ローリングカレンダー(1週間 / 2週間 / 1か月 / 1年)",
"strat_editor_help": "「概要」ビューは常に最初に表示されます。空のグループはスキップされます。",
"picker_name_calendar": "Maintenance Supporter — カレンダー",
"picker_name_panel": "Maintenance Supporter — パネルカード",
"picker_name_budget": "Maintenance Supporter — メンテナンス予算",
"picker_name_groups": "Maintenance Supporter — グループ",
"picker_name_vacation": "Maintenance Supporter — 休暇モード",
"picker_name_battery_fleet": "電池フリート",
"picker_name_section": "Maintenance Supporter — セクション",
"picker_name_vacation_status": "Maintenance Supporter — 休暇ステータス",
"picker_name_budget_status": "Maintenance Supporter — 予算ステータス",
"picker_desc_card": "クイック操作付きのメンテナンスタスク概要。",
"picker_desc_calendar": "7/14/30/365 日の期間、ソースアイコン、予測信頼度バッジを備えたメンテナンスタスクのローリングカレンダー。",
"picker_desc_panel": "Maintenance Supporter パネル全体をカードとして表示 — サイドバー項目なしでパネルビューやダッシュボードのサブビューに。",
"picker_desc_budget": "月間・年間予算をダッシュボード上で直接編集します。",
"picker_desc_groups": "ダッシュボード上でタスクグループを直接作成・名前変更・削除します。",
"picker_desc_vacation": "ダッシュボード上で休暇モードのオン/オフと期間を直接設定します。",
"picker_desc_battery_fleet": "追跡中のすべての電池:今残量が少ないもの、まもなく切れるもの、購入が必要なもの。",
"picker_desc_dashboard": "自動生成されるダッシュボード。ビューをエリア・状態・フロア・期限でグループ化できます(ストラテジーエディターまたは YAML で選択)。",
"picker_desc_section": "メンテナンスタスク(エリア・状態・期限で絞り込み可)を任意のダッシュボードビューにセクションとして埋め込みます。",
"picker_desc_vacation_status": "休暇モードの状態を示すコンパクトなバナー。タップするとパネルで設定を開きます。",
"picker_desc_budget_status": "月間・年間のメンテナンス予算の概要。タップで詳細を表示します。",
"picker_desc_groups_status": "設定済みのタスクグループとそれぞれのタスク数の一覧。",
"hist_note_button": "ダッシュボードのボタンで完了",
"hist_note_todo": "ToDoリストから完了",
"hist_note_voice": "音声で完了",
"hist_note_notification": "通知から完了",
"hist_note_nfc": "NFCタグで完了",
"hist_note_shopping_list": "買い物リストから完了",
"hist_note_todo_mirror": "ミラーしたToDoリストから完了",
"hist_note_skipped_notification": "通知からスキップ",
"hist_note_skipped_button": "ダッシュボードのボタンでスキップ",
"hist_note_sensor_triggered": "センサートリガーが発生",
"hist_note_initial": "タスク作成時に設定された初期値",
"hist_note_auto_recovered": "自動完了:センサーが回復({value})",
"hist_note_reset": "{date} にリセット",
"hist_note_trigger_replaced": "トリガーエンティティを置換: {old} → {new}",
"hist_note_trigger_removed": "センサートリガーを削除しました(エンティティ: {entity})。スケジュールを{schedule}に変更しました。",
"hist_note_compound_removed": "複合トリガーからエンティティ {entity} を削除しました。残りの条件: {count}。",
"recent_activities": "最近のアクティビティ",
"search_notes": "メモを検索",
"avg_cost": "平均費用",
@@ -703,6 +782,7 @@
"on_complete_action_desc": "タスク完了時にHAサービスを呼び出します — 例: デバイスのカウンターをリセット。",
"on_complete_action_target": "対象エンティティ",
"on_complete_action_target_hint": "注意: エンティティのドメインはサービスと一致する必要があります — 例: 'button.press'はbutton.*のみ、'counter.increment'はcounter.*のみ、'input_button.press'はinput_button.*のみで動作します。不一致の場合、アクションは何も表示せずに失敗します(HAのログに「Referenced entities ... missing or not currently available」と記録されます)。",
"on_complete_action_skip_auto": "タスクが自動で完了したとき(センサーが回復したとき)は実行しない",
"on_complete_action_data": "データ(JSON、任意)",
"on_complete_action_test": "設定を検証",
"on_complete_action_test_success": "✓ 設定は有効です(アクションはタスク完了時のみ実行されます)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "「{name}」: タスク {before} → {after}",
"adopt_problem_adopt": "選択を取り込む",
"adopt_problem_done": "{tasks} 件の問題センサーを取り込みました",
"adopt_problem_done_one": "1 件の問題センサーを取り込みました",
"views_label": "ビュー",
"views_none": "— ビューなし —",
"views_manage": "ビューを保存/管理",
@@ -1013,6 +1094,7 @@
"setups_none": "未接続の消耗品センサーを持つ対応デバイスは見つかりませんでした。",
"setups_adopt": "選択項目をセットアップ",
"setups_done": "センサー接続タスクを{tasks}件作成しました。",
"setups_done_one": "センサー接続タスクを1件作成しました。",
"complete_parts_used": "今回使用した部品",
"part_delete_confirm": "部品「{name}」を削除しますか?在庫管理、タスクとのリンク、未完了の購入リマインダーが削除されます。",
"baseline_start_value": "開始値(任意)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "別のデバイスで同じ機種",
"setups_maybe_covered": "「{name}」としてすでにある可能性",
"setups_already": "すでにあり: {names}",
"setups_reset_line": "完了するとインテグレーションのカウンターもリセットします(「{button}」)",
"setups_reset_enable": "— そのためにボタンを有効にします",
"reset_offers_title": "完了時にカウンターをリセット",
"reset_offers_hint": "これらのタスクは、インテグレーション自体が管理するカウンターを監視しています。連携すると、ここでタスクを完了したときにインテグレーションのリセットボタンも押します — そうしないとカウンターが進み続け、タスクがすぐにまた期限になります。",
"reset_offers_before": "現在: 完了しても {integration} のカウンターは進み続けます",
"reset_offers_after": "変更後: 「{button}」も押します",
"reset_offers_renamed": "設定後に名前が変更されています — このタスクがこの部品用か確認してください",
"reset_offers_apply": "リセットを連携",
"reset_offers_done": "{count}件のタスクが完了時にカウンターをリセットするようになりました",
"reset_offers_done_one": "1件のタスクが完了時にカウンターをリセットするようになりました",
"setups_before_after": "オブジェクトのタスク: {before} → {after}",
"setups_new_object_count": "新しいオブジェクト「{name}」· タスク: {count}",
"schedule_preview_title": "次回の予定日",
@@ -1046,6 +1138,7 @@
"history_auto": "自動",
"battery_fleet_title": "電池フリート",
"battery_fleet_none_low": "すべての電池は正常 — 交換不要。",
"battery_fleet_progress": "交換対象:{n}",
"battery_fleet_buy_now": "今すぐ購入",
"battery_fleet_soon": "まもなく必要",
"battery_fleet_soon_hint": "前回交換日からの予測 — 早めに注文を。",
@@ -1099,8 +1192,12 @@
"dismiss": "閉じる",
"gs_label": "はじめに — セットアップが進むとこれらのヒントは消えます",
"gs_setups_chip": "推奨セットアップ:トリガー設定済みのデバイスを{n}台検出",
"gs_setups_chip_one": "推奨セットアップ:トリガー設定済みのデバイスを1台検出",
"gs_adopt_chip": "{n}個の問題センサーをメンテナンスタスクにできます",
"gs_adopt_chip_one": "1個の問題センサーをメンテナンスタスクにできます",
"gs_fleet_chip": "ワンクリックで電池フリートを設定",
"gs_reset_chip": "{n}件のタスクがインテグレーションのカウンターをリセットできます",
"gs_reset_chip_one": "1件のタスクがインテグレーションのカウンターをリセットできます",
"cal_editor_window": "既定の期間",
"cal_editor_window_week": "1週間(7日)",
"cal_editor_window_fortnight": "2週間(14日)",
@@ -243,6 +243,26 @@
"sort_task_name": "작업 이름",
"all_objects": "모든 객체",
"all_parts": "모든 부품",
"all_areas": "모든 구역",
"areas_filter": "구역 필터…",
"areas_filter_none": "필터와 일치하는 구역이 없습니다.",
"area_cost_year": "{year}년 비용",
"area_object_count": "객체 {n}개",
"area_object_count_one": "객체 1개",
"area_range_12m": "최근 12개월",
"area_range_all": "전체 기간",
"area_task_filter": "작업으로 필터…",
"area_kpi_completions": "완료 횟수",
"area_kpi_total_time": "총 소요 시간",
"area_cost_per_month": "월별 비용",
"area_cost_per_year": "연도별 비용",
"area_cost_per_object": "객체별 비용",
"area_cost_share": "비율",
"area_history_section": "기록(전체 객체)",
"area_report_print": "구역 보고서(PDF)",
"area_report_title": "구역 보고서",
"area_report_period": "기간",
"area_no_costs": "이 기간에 기록된 비용이 없습니다.",
"no_tasks_yet": "아직 작업 없음",
"add_first_task": "첫 작업 추가",
"trigger_configuration": "트리거 설정",
@@ -401,11 +421,70 @@
"qr_download": "SVG 다운로드",
"qr_action_view": "유지보수 정보 보기",
"qr_action_complete": "유지보수 완료로 표시",
"qr_action_quick_complete": "빠른 완료 — 대화 상자 없이",
"qr_print_quick_none": "빠른 완료 코드에는 작업의 빠른 완료 기본값이 필요합니다(작업 대화 상자 → 빠른 완료 기본값). 선택한 개체의 작업에는 아직 없습니다.",
"qr_url_mode": "링크 유형",
"qr_mode_companion": "Companion 앱",
"qr_mode_local": "로컬 (mDNS)",
"qr_mode_server": "서버 URL",
"overview": "개요",
"strat_this_month": "이번 달",
"strat_later": "이후",
"strat_week": "1주",
"strat_fortnight": "2주",
"strat_month": "1개월",
"strat_year": "1년",
"strat_other": "기타",
"strat_status": "상태",
"strat_empty_title": "아직 유지보수 객체가 없습니다",
"strat_empty_content": "유지보수 패널을 열어 첫 객체를 추가하세요 — 수영장 펌프, 공조 필터, 차량 등 정기 관리가 필요한 모든 것.",
"strat_open_panel": "유지보수 패널 열기",
"strat_add_object": "객체 추가",
"strat_not_loaded": "**Maintenance Supporter**가 로드되지 않았습니다. 먼저 통합구성요소를 설치/활성화하세요.",
"strat_group_by": "뷰 그룹화 기준",
"strat_group_area": "구역별 (기본)",
"strat_group_status": "상태별 (기한 초과 / 트리거됨 / 기한 임박 / 정상)",
"strat_group_floor": "층별 (HA 층 사용)",
"strat_group_due_date": "예정일별 (기한 초과 / 오늘 / 이번 주 / 이번 달 / 이후)",
"strat_group_calendar": "롤링 캘린더 (1주 / 2주 / 1개월 / 1년)",
"strat_editor_help": "'개요' 뷰는 항상 맨 처음에 표시됩니다. 빈 그룹은 건너뜁니다.",
"picker_name_calendar": "Maintenance Supporter — 캘린더",
"picker_name_panel": "Maintenance Supporter — 패널 카드",
"picker_name_budget": "Maintenance Supporter — 유지보수 예산",
"picker_name_groups": "Maintenance Supporter — 그룹",
"picker_name_vacation": "Maintenance Supporter — 휴가 모드",
"picker_name_battery_fleet": "배터리 플릿",
"picker_name_section": "Maintenance Supporter — 섹션",
"picker_name_vacation_status": "Maintenance Supporter — 휴가 상태",
"picker_name_budget_status": "Maintenance Supporter — 예산 상태",
"picker_desc_card": "빠른 작업이 포함된 유지보수 작업 개요.",
"picker_desc_calendar": "7/14/30/365일 기간, 출처 아이콘, 예측 신뢰도 배지를 갖춘 유지보수 작업 롤링 캘린더.",
"picker_desc_panel": "전체 Maintenance Supporter 패널을 카드로 — 사이드바 항목 없이 패널 뷰 / 대시보드 하위 뷰에 사용.",
"picker_desc_budget": "대시보드에서 바로 월 예산과 연 예산을 편집합니다.",
"picker_desc_groups": "대시보드에서 바로 작업 그룹을 만들고, 이름을 바꾸고, 삭제합니다.",
"picker_desc_vacation": "대시보드에서 바로 휴가 모드를 켜거나 끄고 기간을 설정합니다.",
"picker_desc_battery_fleet": "추적 중인 모든 배터리: 지금 부족한 것, 곧 떨어질 것, 구매할 것.",
"picker_desc_dashboard": "자동 생성 대시보드. 뷰를 구역, 상태, 층 또는 예정일별로 그룹화하며, 전략 편집기나 YAML에서 선택합니다.",
"picker_desc_section": "유지보수 작업(구역, 상태, 예정일로 필터 가능)을 모든 대시보드 뷰에 섹션으로 삽입합니다.",
"picker_desc_vacation_status": "휴가 모드 상태를 보여 주는 간결한 배너. 탭하면 패널에서 설정이 열립니다.",
"picker_desc_budget_status": "월별·연별 유지보수 예산 개요. 탭하면 자세한 내용을 볼 수 있습니다.",
"picker_desc_groups_status": "구성된 작업 그룹 목록과 그룹별 작업 수.",
"hist_note_button": "대시보드 버튼으로 완료됨",
"hist_note_todo": "할 일 목록에서 완료됨",
"hist_note_voice": "음성으로 완료됨",
"hist_note_notification": "알림에서 완료됨",
"hist_note_nfc": "NFC 태그로 완료됨",
"hist_note_shopping_list": "쇼핑 목록에서 완료됨",
"hist_note_todo_mirror": "미러링된 할 일 목록에서 완료됨",
"hist_note_skipped_notification": "알림에서 건너뜀",
"hist_note_skipped_button": "대시보드 버튼으로 건너뜀",
"hist_note_sensor_triggered": "센서 트리거 활성화됨",
"hist_note_initial": "작업 생성 시 설정된 초기값",
"hist_note_auto_recovered": "자동 완료: 센서가 정상으로 돌아옴 ({value})",
"hist_note_reset": "{date}(으)로 초기화됨",
"hist_note_trigger_replaced": "트리거 엔티티 교체됨: {old} → {new}",
"hist_note_trigger_removed": "센서 트리거 제거됨 (엔티티: {entity}). 일정이 {schedule}(으)로 변경됨.",
"hist_note_compound_removed": "복합 트리거에서 엔티티 {entity} 제거됨. 남은 조건: {count}개.",
"recent_activities": "최근 활동",
"search_notes": "메모 검색",
"avg_cost": "평균 비용",
@@ -709,6 +788,7 @@
"on_complete_action_desc": "작업이 완료되면 HA 서비스를 호출합니다 — 예: 기기의 카운터 초기화.",
"on_complete_action_target": "대상 엔티티",
"on_complete_action_target_hint": "참고: 엔티티 도메인이 서비스와 일치해야 합니다 — 예: 'button.press'는 button.*에서만, 'counter.increment'는 counter.*에서만, 'input_button.press'는 input_button.*에서만 동작합니다. 불일치 시 동작이 조용히 실패합니다 (HA 로그에 'Referenced entities ... missing or not currently available' 기록).",
"on_complete_action_skip_auto": "작업이 스스로 완료될 때는 제외 (센서가 정상으로 돌아옴)",
"on_complete_action_data": "데이터 (JSON, 선택)",
"on_complete_action_test": "설정 검증",
"on_complete_action_test_success": "✓ 설정이 유효합니다 (동작은 작업 완료 시에만 실행됩니다)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "\"{name}\": 작업 {before} → {after}",
"adopt_problem_adopt": "선택 항목 가져오기",
"adopt_problem_done": "문제 센서 {tasks}개를 가져왔습니다",
"adopt_problem_done_one": "문제 센서 1개를 가져왔습니다",
"views_label": "보기",
"views_none": "— 보기 없음 —",
"views_manage": "보기 저장 / 관리",
@@ -1013,6 +1094,7 @@
"setups_none": "연결되지 않은 소모품 센서를 가진 지원 기기를 찾지 못했습니다.",
"setups_adopt": "선택 항목 설정",
"setups_done": "센서 연결 작업 {tasks}개가 생성되었습니다.",
"setups_done_one": "센서 연결 작업 1개가 생성되었습니다.",
"complete_parts_used": "이번에 사용한 부품",
"part_delete_confirm": "'{name}' 부품을 삭제할까요? 재고 추적, 작업 연결, 진행 중인 구매 알림이 모두 제거됩니다.",
"baseline_start_value": "시작 값 (선택)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "다른 기기의 같은 모델",
"setups_maybe_covered": "이미 \"{name}\"(으)로 있을 수 있음",
"setups_already": "이미 있음: {names}",
"setups_reset_line": "완료하면 통합구성요소의 카운터도 초기화합니다 (“{button}”)",
"setups_reset_enable": "— 이를 위해 버튼이 활성화됩니다",
"reset_offers_title": "완료 시 카운터 초기화",
"reset_offers_hint": "이 작업들은 통합구성요소가 직접 관리하는 카운터를 감시합니다. 연결하면 여기서 작업을 완료할 때 통합구성요소의 초기화 버튼도 누릅니다 — 그렇지 않으면 카운터가 계속 올라가 작업이 곧바로 다시 기한이 됩니다.",
"reset_offers_before": "현재: 완료해도 {integration}의 카운터는 계속 올라갑니다",
"reset_offers_after": "변경 후: “{button}”도 누릅니다",
"reset_offers_renamed": "설정 후 이름이 변경됨 — 이 작업이 이 부품에 해당하는지 확인하세요",
"reset_offers_apply": "초기화 연결",
"reset_offers_done": "작업 {count}개가 이제 완료 시 카운터를 초기화합니다",
"reset_offers_done_one": "작업 1개가 이제 완료 시 카운터를 초기화합니다",
"setups_before_after": "객체의 작업: {before} → {after}",
"setups_new_object_count": "새 객체 \"{name}\" · 작업: {count}",
"schedule_preview_title": "다음 예정일",
@@ -1046,6 +1138,7 @@
"history_auto": "자동",
"battery_fleet_title": "배터리 플릿",
"battery_fleet_none_low": "모든 배터리 정상 — 교체할 것이 없습니다.",
"battery_fleet_progress": "교체 대상: {n}",
"battery_fleet_buy_now": "지금 구매",
"battery_fleet_soon": "곧 필요",
"battery_fleet_soon_hint": "마지막 교체일로부터 예측한 값입니다 — 미리 주문해 두세요.",
@@ -1099,8 +1192,12 @@
"dismiss": "닫기",
"gs_label": "시작하기 — 설정이 늘어나면 이 힌트는 사라집니다",
"gs_setups_chip": "추천 설정: 트리거가 준비된 기기 {n}대 발견",
"gs_setups_chip_one": "추천 설정: 트리거가 준비된 기기 1대 발견",
"gs_adopt_chip": "문제 센서 {n}개를 유지보수 작업으로 만들 수 있습니다",
"gs_adopt_chip_one": "문제 센서 1개를 유지보수 작업으로 만들 수 있습니다",
"gs_fleet_chip": "클릭 한 번으로 배터리 플릿 설정",
"gs_reset_chip": "작업 {n}개가 통합구성요소의 카운터를 초기화할 수 있습니다",
"gs_reset_chip_one": "작업 1개가 통합구성요소의 카운터를 초기화할 수 있습니다",
"cal_editor_window": "기본 기간",
"cal_editor_window_week": "1주 (7일)",
"cal_editor_window_fortnight": "2주 (14일)",
@@ -243,6 +243,26 @@
"sort_task_name": "Oppgavenavn",
"all_objects": "Alle objekter",
"all_parts": "Alle deler",
"all_areas": "Alle områder",
"areas_filter": "Filtrer områder…",
"areas_filter_none": "Ingen områder samsvarer med filteret.",
"area_cost_year": "Kostnad {year}",
"area_object_count": "{n} objekter",
"area_object_count_one": "1 objekt",
"area_range_12m": "Siste 12 måneder",
"area_range_all": "Hele perioden",
"area_task_filter": "Filtrer etter oppgave…",
"area_kpi_completions": "Utførelser",
"area_kpi_total_time": "Total tid",
"area_cost_per_month": "Kostnad per måned",
"area_cost_per_year": "Kostnad per år",
"area_cost_per_object": "Kostnad per objekt",
"area_cost_share": "Andel",
"area_history_section": "Historikk (alle objekter)",
"area_report_print": "Områderapport (PDF)",
"area_report_title": "Områderapport",
"area_report_period": "Periode",
"area_no_costs": "Ingen kostnader registrert i denne perioden.",
"no_tasks_yet": "Ingen oppgaver ennå",
"add_first_task": "Legg til første oppgave",
"trigger_configuration": "Utløserkonfigurasjon",
@@ -401,11 +421,70 @@
"qr_download": "Last ned SVG",
"qr_action_view": "Vis vedlikeholdsinfo",
"qr_action_complete": "Merk vedlikehold som fullført",
"qr_action_quick_complete": "Hurtigfullføring — uten dialog",
"qr_print_quick_none": "Hurtigfullføringskoder krever standardverdier for hurtigfullføring på en oppgave (oppgavedialogen → Standardverdier for hurtigfullføring) — ingen oppgave i de valgte objektene har noen ennå.",
"qr_url_mode": "Lenketype",
"qr_mode_companion": "Companion-app",
"qr_mode_local": "Lokal (mDNS)",
"qr_mode_server": "Server-URL",
"overview": "Oversikt",
"strat_this_month": "Denne måneden",
"strat_later": "Senere",
"strat_week": "Uke",
"strat_fortnight": "To uker",
"strat_month": "Måned",
"strat_year": "År",
"strat_other": "Annet",
"strat_status": "Tilstand",
"strat_empty_title": "Ingen vedlikeholdsobjekter ennå",
"strat_empty_content": "Åpne vedlikeholdspanelet for å legge til ditt første objekt — bassengpumpe, ventilasjonsfilter, kjøretøy, alt som trenger planlagt stell.",
"strat_open_panel": "Åpne vedlikeholdspanelet",
"strat_add_object": "Legg til objekt",
"strat_not_loaded": "**Maintenance Supporter** er ikke lastet inn. Installer/aktiver integrasjonen først.",
"strat_group_by": "Grupper visninger etter",
"strat_group_area": "Etter område (standard)",
"strat_group_status": "Etter status (Forfalt / Utløst / Forfaller snart / OK)",
"strat_group_floor": "Etter etasje (bruker HA-etasjene)",
"strat_group_due_date": "Etter forfallsdato (Forfalt / I dag / Denne uken / Denne måneden / Senere)",
"strat_group_calendar": "Rullerende kalender (Uke / To uker / Måned / År)",
"strat_editor_help": "Visningen «Oversikt» kommer alltid først. Tomme grupper hoppes over.",
"picker_name_calendar": "Maintenance Supporter — Kalender",
"picker_name_panel": "Maintenance Supporter — Panel som kort",
"picker_name_budget": "Maintenance Supporter — Vedlikeholdsbudsjett",
"picker_name_groups": "Maintenance Supporter — Grupper",
"picker_name_vacation": "Maintenance Supporter — Feriemodus",
"picker_name_battery_fleet": "Batteriflåte",
"picker_name_section": "Maintenance Supporter — Seksjon",
"picker_name_vacation_status": "Maintenance Supporter — Feriestatus",
"picker_name_budget_status": "Maintenance Supporter — Budsjettstatus",
"picker_desc_card": "Oversikt over vedlikeholdsoppgavene dine med hurtighandlinger.",
"picker_desc_calendar": "Rullerende kalender over vedlikeholdsoppgaver med vinduer på 7/14/30/365 dager, kildeikoner og merker for prognosesikkerhet.",
"picker_desc_panel": "Hele Maintenance Supporter-panelet som kort — for en panelvisning / undervisning i et dashbord uten oppføring i sidefeltet.",
"picker_desc_budget": "Rediger måneds- og årsbudsjettet rett på dashbordet.",
"picker_desc_groups": "Opprett, gi nytt navn til og slett oppgavegrupper rett på dashbordet.",
"picker_desc_vacation": "Slå feriemodus av eller på og angi datoene rett på dashbordet.",
"picker_desc_battery_fleet": "Alle overvåkede batterier: hva som er lavt nå, hva som snart går tomt, og hva du må kjøpe.",
"picker_desc_dashboard": "Automatisk generert dashbord. Grupper visninger etter område, status, etasje eller forfallsdato — velges i strategiredigeringen eller i YAML.",
"picker_desc_section": "Bygg inn vedlikeholdsoppgaver (kan filtreres etter område, status, forfallsdato) som en seksjon i hvilken som helst dashbordvisning.",
"picker_desc_vacation_status": "Kompakt statusbanner for feriemodus. Trykk for å åpne innstillingene i panelet.",
"picker_desc_budget_status": "Oversikt over måneds- og årsbudsjettet for vedlikehold. Trykk for detaljer.",
"picker_desc_groups_status": "Liste over konfigurerte oppgavegrupper med antall oppgaver i hver.",
"hist_note_button": "Fullført via knapp på dashbordet",
"hist_note_todo": "Fullført fra gjøremålslisten",
"hist_note_voice": "Fullført med tale",
"hist_note_notification": "Fullført fra varselet",
"hist_note_nfc": "Fullført via NFC-brikke",
"hist_note_shopping_list": "Fullført fra handlelisten",
"hist_note_todo_mirror": "Fullført fra en speilet gjøremålsliste",
"hist_note_skipped_notification": "Hoppet over fra varselet",
"hist_note_skipped_button": "Hoppet over via knapp på dashbordet",
"hist_note_sensor_triggered": "Sensorutløser aktivert",
"hist_note_initial": "Startverdi satt da oppgaven ble opprettet",
"hist_note_auto_recovered": "Automatisk fullført: sensoren er normal igjen ({value})",
"hist_note_reset": "Tilbakestilt til {date}",
"hist_note_trigger_replaced": "Utløserentitet erstattet: {old} → {new}",
"hist_note_trigger_removed": "Sensorutløser fjernet (entitet: {entity}). Tidsplan endret til {schedule}.",
"hist_note_compound_removed": "Entiteten {entity} fjernet fra den sammensatte utløseren; gjenværende betingelser: {count}.",
"recent_activities": "Nylige aktiviteter",
"search_notes": "Søk i notater",
"avg_cost": "Gj.snittlig kostnad",
@@ -703,6 +782,7 @@
"on_complete_action_desc": "Kaller en HA-tjeneste når oppgaven fullføres — f.eks. tilbakestille en teller på enheten.",
"on_complete_action_target": "Målentitet",
"on_complete_action_target_hint": "Merk: entitetsdomenet må samsvare med tjenesten — f.eks. fungerer 'button.press' kun på button.*, 'counter.increment' kun på counter.*, 'input_button.press' kun på input_button.* osv. Ved manglende samsvar mislykkes handlingen i det stille (HA logger 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Ikke når oppgaven fullføres av seg selv (sensoren er normal igjen)",
"on_complete_action_data": "Data (JSON, valgfritt)",
"on_complete_action_test": "Valider konfigurasjon",
"on_complete_action_test_success": "✓ Konfigurasjon gyldig (handlingen utløses kun ved fullføring av oppgaven)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "«{name}»: oppgaver {before} → {after}",
"adopt_problem_adopt": "Ta i bruk valgte",
"adopt_problem_done": "Tok i bruk {tasks} problemsensor(er)",
"adopt_problem_done_one": "Tok i bruk 1 problemsensor",
"views_label": "Visninger",
"views_none": "— Ingen visning —",
"views_manage": "Lagre / administrer visninger",
@@ -1013,6 +1094,7 @@
"setups_none": "Ingen støttede enheter med ukoblede forbrukssensorer funnet.",
"setups_adopt": "Sett opp valgte",
"setups_done": "{tasks} sensor-koblede oppgaver opprettet.",
"setups_done_one": "1 sensor-koblet oppgave opprettet.",
"complete_parts_used": "Deler brukt denne gangen",
"part_delete_confirm": "Slette delen '{name}'? Lagersporing, oppgavekoblinger og en åpen kjøpspåminnelse fjernes.",
"baseline_start_value": "Startverdi (valgfritt)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "samme modell på en annen enhet",
"setups_maybe_covered": "Finnes sannsynligvis allerede som «{name}»",
"setups_already": "Finnes allerede: {names}",
"setups_reset_line": "Fullføring tilbakestiller også telleren i integrasjonen («{button}»)",
"setups_reset_enable": "— knappen slås på for dette",
"reset_offers_title": "Tilbakestill tellere ved fullføring",
"reset_offers_hint": "Disse oppgavene følger med på en teller som integrasjonen fører selv. Når de er koblet, trykker en fullføring her også på integrasjonens tilbakestillingsknapp — ellers fortsetter telleren å gå, og oppgaven forfaller straks igjen.",
"reset_offers_before": "Nå: fullføring lar telleren i {integration} gå videre",
"reset_offers_after": "Deretter: trykker også på «{button}»",
"reset_offers_renamed": "Omdøpt siden oppsettet — sjekk at oppgaven gjelder denne delen",
"reset_offers_apply": "Koble tilbakestillinger",
"reset_offers_done": "{count} oppgaver tilbakestiller nå telleren ved fullføring",
"reset_offers_done_one": "1 oppgave tilbakestiller nå telleren ved fullføring",
"setups_before_after": "Oppgaver på objektet: {before} → {after}",
"setups_new_object_count": "Nytt objekt «{name}» · oppgaver: {count}",
"schedule_preview_title": "Kommende datoer",
@@ -1046,6 +1138,7 @@
"history_auto": "Automatisk",
"battery_fleet_title": "Batteriflåte",
"battery_fleet_none_low": "Alle batterier OK — ingenting å bytte.",
"battery_fleet_progress": "{n} må byttes",
"battery_fleet_buy_now": "Kjøp nå",
"battery_fleet_soon": "Trengs snart",
"battery_fleet_soon_hint": "Estimert fra siste bytte — bestill i forkant.",
@@ -1099,8 +1192,12 @@
"dismiss": "Avvis",
"gs_label": "Kom i gang — tipsene forsvinner etter hvert som oppsettet vokser",
"gs_setups_chip": "Foreslåtte oppsett fant {n} enheter med ferdigkoblede utløsere",
"gs_setups_chip_one": "Foreslåtte oppsett fant 1 enhet med ferdigkoblede utløsere",
"gs_adopt_chip": "{n} problemsensorer kan bli vedlikeholdsoppgaver",
"gs_adopt_chip_one": "1 problemsensor kan bli en vedlikeholdsoppgave",
"gs_fleet_chip": "Ett klikk setter opp batteriflåten",
"gs_reset_chip": "{n} oppgaver kan tilbakestille telleren i integrasjonen",
"gs_reset_chip_one": "1 oppgave kan tilbakestille telleren i integrasjonen",
"cal_editor_window": "Standardvindu",
"cal_editor_window_week": "Uke (7 dager)",
"cal_editor_window_fortnight": "To uker (14 dager)",
@@ -241,6 +241,26 @@
"sort_task_name": "Taaknaam",
"all_objects": "Alle objecten",
"all_parts": "Alle onderdelen",
"all_areas": "Alle gebieden",
"areas_filter": "Gebieden filteren…",
"areas_filter_none": "Geen gebied komt overeen met het filter.",
"area_cost_year": "Kosten {year}",
"area_object_count": "{n} objecten",
"area_object_count_one": "Eén object",
"area_range_12m": "Afgelopen 12 maanden",
"area_range_all": "Volledige periode",
"area_task_filter": "Filteren op taak…",
"area_kpi_completions": "Voltooiingen",
"area_kpi_total_time": "Totale tijd",
"area_cost_per_month": "Kosten per maand",
"area_cost_per_year": "Kosten per jaar",
"area_cost_per_object": "Kosten per object",
"area_cost_share": "Aandeel",
"area_history_section": "Geschiedenis (alle objecten)",
"area_report_print": "Gebiedsrapport (PDF)",
"area_report_title": "Gebiedsrapport",
"area_report_period": "Periode",
"area_no_costs": "Geen kosten geregistreerd in deze periode.",
"no_tasks_yet": "Nog geen taken",
"add_first_task": "Eerste taak toevoegen",
"trigger_configuration": "Triggerconfiguratie",
@@ -399,11 +419,70 @@
"qr_download": "SVG downloaden",
"qr_action_view": "Onderhoudsinfo bekijken",
"qr_action_complete": "Onderhoud als voltooid markeren",
"qr_action_quick_complete": "Snel voltooien — zonder dialoogvenster",
"qr_print_quick_none": "Snel-voltooien-codes hebben snel-voltooien-standaardwaarden bij een taak nodig (taakdialoog → Snel-voltooien-standaardwaarden) — nog geen taak van de gekozen objecten heeft ze.",
"qr_url_mode": "Linktype",
"qr_mode_companion": "Companion App",
"qr_mode_local": "Lokaal (mDNS)",
"qr_mode_server": "Server-URL",
"overview": "Overzicht",
"strat_this_month": "Deze maand",
"strat_later": "Daarna",
"strat_week": "Komende week",
"strat_fortnight": "Komende twee weken",
"strat_month": "Komende maand",
"strat_year": "Komend jaar",
"strat_other": "Overig",
"strat_status": "Stand van zaken",
"strat_empty_title": "Nog geen onderhoudsobjecten",
"strat_empty_content": "Open het Onderhoudspaneel om je eerste object toe te voegen — zwembadpomp, ventilatiefilter, voertuig, alles wat gepland onderhoud nodig heeft.",
"strat_open_panel": "Onderhoudspaneel openen",
"strat_add_object": "Object toevoegen",
"strat_not_loaded": "**Maintenance Supporter** is niet geladen. Installeer/activeer eerst de integratie.",
"strat_group_by": "Weergaven groeperen op",
"strat_group_area": "Per gebied (standaard)",
"strat_group_status": "Op status (Achterstallig / Geactiveerd / Binnenkort / OK)",
"strat_group_floor": "Per verdieping (gebruikt de HA-verdiepingen)",
"strat_group_due_date": "Op vervaldatum (Achterstallig / Vandaag / Deze week / Deze maand / Daarna)",
"strat_group_calendar": "Doorlopende kalender (komende week / twee weken / maand / jaar)",
"strat_editor_help": "De weergave \"Overzicht\" staat altijd vooraan. Lege groepen worden overgeslagen.",
"picker_name_calendar": "Maintenance Supporter — Kalender",
"picker_name_panel": "Maintenance Supporter — Paneel als kaart",
"picker_name_budget": "Maintenance Supporter — Onderhoudsbudget",
"picker_name_groups": "Maintenance Supporter — Groepen",
"picker_name_vacation": "Maintenance Supporter — Vakantiemodus",
"picker_name_battery_fleet": "Batterijvloot",
"picker_name_section": "Maintenance Supporter — Sectie",
"picker_name_vacation_status": "Maintenance Supporter — Vakantiestatus",
"picker_name_budget_status": "Maintenance Supporter — Budgetstatus",
"picker_desc_card": "Overzicht van je onderhoudstaken met snelle acties.",
"picker_desc_calendar": "Doorlopende kalender van onderhoudstaken met vensters van 7/14/30/365 dagen, bronpictogrammen en labels voor de voorspellingszekerheid.",
"picker_desc_panel": "Het volledige Maintenance Supporter-paneel als kaart — voor een paneelweergave / dashboard-subweergave zonder item in de zijbalk.",
"picker_desc_budget": "Bewerk het maand- en jaarbudget direct op het dashboard.",
"picker_desc_groups": "Maak, hernoem en verwijder taakgroepen direct op het dashboard.",
"picker_desc_vacation": "Zet de vakantiemodus aan of uit en stel de data in, direct op het dashboard.",
"picker_desc_battery_fleet": "Alle gevolgde batterijen: wat nu bijna leeg is, wat binnenkort opraakt en wat je moet kopen.",
"picker_desc_dashboard": "Automatisch gegenereerd dashboard. Groepeer weergaven op gebied, status, verdieping of vervaldatum — te kiezen in de strategie-editor of via YAML.",
"picker_desc_section": "Sluit onderhoudstaken (filterbaar op gebied, status, vervaldatum) in als sectie in elke dashboardweergave.",
"picker_desc_vacation_status": "Compacte statusbalk voor de vakantiemodus. Tik om de instellingen in het paneel te openen.",
"picker_desc_budget_status": "Overzicht van het maand- en jaarbudget voor onderhoud. Tik voor details.",
"picker_desc_groups_status": "Lijst van ingestelde taakgroepen met het aantal taken per groep.",
"hist_note_button": "Voltooid via dashboardknop",
"hist_note_todo": "Voltooid via de takenlijst",
"hist_note_voice": "Voltooid via spraak",
"hist_note_notification": "Voltooid via de melding",
"hist_note_nfc": "Voltooid via NFC-tag",
"hist_note_shopping_list": "Voltooid via de boodschappenlijst",
"hist_note_todo_mirror": "Voltooid via een gespiegelde takenlijst",
"hist_note_skipped_notification": "Overgeslagen via de melding",
"hist_note_skipped_button": "Overgeslagen via dashboardknop",
"hist_note_sensor_triggered": "Sensortrigger geactiveerd",
"hist_note_initial": "Beginwaarde ingesteld bij het aanmaken van de taak",
"hist_note_auto_recovered": "Automatisch voltooid: sensor hersteld ({value})",
"hist_note_reset": "Teruggezet naar {date}",
"hist_note_trigger_replaced": "Trigger-entiteit vervangen: {old} → {new}",
"hist_note_trigger_removed": "Sensortrigger verwijderd (entiteit was: {entity}). Schema omgezet naar {schedule}.",
"hist_note_compound_removed": "Entiteit {entity} verwijderd uit samengestelde trigger; resterende voorwaarden: {count}.",
"recent_activities": "Recente activiteiten",
"search_notes": "Notities doorzoeken",
"avg_cost": "Ø Kosten",
@@ -716,6 +795,7 @@
"groups_manage_tasks": "Taaktoewijzingen beheren…",
"groups_open_panel": "Openen in paneel",
"on_complete_action_target_hint": "Let op: het domein van de entiteit moet bij de service passen — bijv. 'button.press' werkt alleen op button.*, 'counter.increment' alleen op counter.*, 'input_button.press' alleen op input_button.* enz. Bij een mismatch mislukt de actie stil (HA logt 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Niet wanneer de taak zichzelf voltooit (de sensor is hersteld)",
"show_all_objects": "Alle objecten tonen",
"show_all_tasks": "Filter wissen — alle taken tonen",
"filter_to_overdue": "Takenlijst filteren op alleen achterstallig",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "\"{name}\": taken {before} → {after}",
"adopt_problem_adopt": "Selectie overnemen",
"adopt_problem_done": "{tasks} probleemsensor(en) overgenomen",
"adopt_problem_done_one": "1 probleemsensor overgenomen",
"views_label": "Weergaven",
"views_none": "— Geen weergave —",
"views_manage": "Weergaven opslaan/beheren",
@@ -1013,6 +1094,7 @@
"setups_none": "Geen ondersteunde apparaten met ongekoppelde verbruikssensoren gevonden.",
"setups_adopt": "Selectie instellen",
"setups_done": "{tasks} sensor-gekoppelde taken aangemaakt.",
"setups_done_one": "1 sensor-gekoppelde taak aangemaakt.",
"complete_parts_used": "Deze keer gebruikte onderdelen",
"part_delete_confirm": "Onderdeel '{name}' verwijderen? De voorraadregistratie, taakkoppelingen en een openstaande koopherinnering worden verwijderd.",
"baseline_start_value": "Startwaarde (optioneel)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "zelfde model op een ander apparaat",
"setups_maybe_covered": "Waarschijnlijk al aanwezig als \"{name}\"",
"setups_already": "Al aanwezig: {names}",
"setups_reset_line": "Voltooien reset ook de teller in de integratie (“{button}”)",
"setups_reset_enable": "— de knop wordt hiervoor ingeschakeld",
"reset_offers_title": "Tellers resetten bij voltooien",
"reset_offers_hint": "Deze taken volgen een teller die de integratie zelf bijhoudt. Gekoppeld drukt het voltooien van de taak hier ook op de resetknop van de integratie — anders loopt de teller door en is de taak meteen weer aan de beurt.",
"reset_offers_before": "Nu: voltooien laat de teller in {integration} doorlopen",
"reset_offers_after": "Daarna: drukt ook op “{button}”",
"reset_offers_renamed": "Hernoemd sinds het instellen — controleer of deze taak bij dit onderdeel hoort",
"reset_offers_apply": "Resets koppelen",
"reset_offers_done": "{count} taken resetten hun teller nu bij voltooien",
"reset_offers_done_one": "1 taak reset haar teller nu bij voltooien",
"setups_before_after": "Taken van het object: {before} → {after}",
"setups_new_object_count": "Nieuw object \"{name}\" · taken: {count}",
"schedule_preview_title": "Volgende data",
@@ -1046,6 +1138,7 @@
"history_auto": "Automatisch",
"battery_fleet_title": "Batterijvloot",
"battery_fleet_none_low": "Alle batterijen OK — niets te vervangen.",
"battery_fleet_progress": "{n} te vervangen",
"battery_fleet_buy_now": "Nu kopen",
"battery_fleet_soon": "Binnenkort nodig",
"battery_fleet_soon_hint": "Geschat op basis van de laatste vervanging — bestel vooruit.",
@@ -1099,8 +1192,12 @@
"dismiss": "Verbergen",
"gs_label": "Aan de slag — deze tips verdwijnen naarmate je installatie groeit",
"gs_setups_chip": "Voorgestelde setups: {n} apparaten met vooraf ingestelde triggers gevonden",
"gs_setups_chip_one": "Voorgestelde setups: 1 apparaat met vooraf ingestelde triggers gevonden",
"gs_adopt_chip": "{n} probleemsensoren kunnen onderhoudstaken worden",
"gs_adopt_chip_one": "1 probleemsensor kan een onderhoudstaak worden",
"gs_fleet_chip": "Eén klik stelt het batterijpark in",
"gs_reset_chip": "{n} taken kunnen hun teller in de integratie resetten",
"gs_reset_chip_one": "1 taak kan haar teller in de integratie resetten",
"cal_editor_window": "Standaardvenster",
"cal_editor_window_week": "Week (7 dagen)",
"cal_editor_window_fortnight": "Twee weken (14 dagen)",
@@ -241,6 +241,26 @@
"sort_task_name": "Nazwa zadania",
"all_objects": "Wszystkie obiekty",
"all_parts": "Wszystkie części",
"all_areas": "Wszystkie obszary",
"areas_filter": "Filtruj obszary…",
"areas_filter_none": "Żaden obszar nie pasuje do filtra.",
"area_cost_year": "Koszt {year}",
"area_object_count": "Obiekty: {n}",
"area_object_count_one": "1 obiekt",
"area_range_12m": "Ostatnie 12 miesięcy",
"area_range_all": "Cały okres",
"area_task_filter": "Filtruj według zadania…",
"area_kpi_completions": "Wykonania",
"area_kpi_total_time": "Łączny czas",
"area_cost_per_month": "Koszt według miesięcy",
"area_cost_per_year": "Koszt według lat",
"area_cost_per_object": "Koszt według obiektów",
"area_cost_share": "Udział",
"area_history_section": "Historia (wszystkie obiekty)",
"area_report_print": "Raport obszaru (PDF)",
"area_report_title": "Raport obszaru",
"area_report_period": "Okres",
"area_no_costs": "Brak zarejestrowanych kosztów w tym okresie.",
"no_tasks_yet": "Jeszcze brak zadań",
"add_first_task": "Dodaj pierwsze zadanie",
"trigger_configuration": "Konfiguracja wyzwalacza",
@@ -399,11 +419,70 @@
"qr_download": "Pobierz SVG",
"qr_action_view": "Wyświetl informacje o konserwacji",
"qr_action_complete": "Oznacz konserwację jako wykonaną",
"qr_action_quick_complete": "Szybkie zakończenie — bez okna dialogowego",
"qr_print_quick_none": "Kody szybkiego zakończenia wymagają wartości domyślnych szybkiego zakończenia w zadaniu (okno zadania → Wartości domyślne szybkiego zakończenia) — żadne zadanie wybranych obiektów ich jeszcze nie ma.",
"qr_url_mode": "Typ linku",
"qr_mode_companion": "Companion App",
"qr_mode_local": "Lokalny (mDNS)",
"qr_mode_server": "URL serwera",
"overview": "Przegląd",
"strat_this_month": "W tym miesiącu",
"strat_later": "Później",
"strat_week": "Tydzień",
"strat_fortnight": "Dwa tygodnie",
"strat_month": "Miesiąc",
"strat_year": "Rok",
"strat_other": "Inne",
"strat_status": "Stan",
"strat_empty_title": "Brak obiektów konserwacji",
"strat_empty_content": "Otwórz panel Konserwacja, aby dodać pierwszy obiekt — pompę basenową, filtr wentylacji, pojazd, wszystko, co wymaga regularnej obsługi.",
"strat_open_panel": "Otwórz panel Konserwacja",
"strat_add_object": "Dodaj obiekt",
"strat_not_loaded": "**Maintenance Supporter** nie jest załadowany. Najpierw zainstaluj/włącz integrację.",
"strat_group_by": "Grupuj widoki wg",
"strat_group_area": "Wg obszaru (domyślnie)",
"strat_group_status": "Wg statusu (Zaległe / Wyzwolone / Wkrótce / OK)",
"strat_group_floor": "Wg piętra (używa pięter HA)",
"strat_group_due_date": "Wg terminu (Zaległe / Dzisiaj / W tym tygodniu / W tym miesiącu / Później)",
"strat_group_calendar": "Kalendarz kroczący (Tydzień / Dwa tygodnie / Miesiąc / Rok)",
"strat_editor_help": "Widok „Przegląd” jest zawsze pierwszy. Puste grupy są pomijane.",
"picker_name_calendar": "Maintenance Supporter — Kalendarz",
"picker_name_panel": "Maintenance Supporter — Panel jako karta",
"picker_name_budget": "Maintenance Supporter — Budżet konserwacji",
"picker_name_groups": "Maintenance Supporter — Grupy",
"picker_name_vacation": "Maintenance Supporter — Tryb urlopowy",
"picker_name_battery_fleet": "Flota baterii",
"picker_name_section": "Maintenance Supporter — Sekcja",
"picker_name_vacation_status": "Maintenance Supporter — Status urlopu",
"picker_name_budget_status": "Maintenance Supporter — Status budżetu",
"picker_desc_card": "Przegląd zadań konserwacyjnych z szybkimi akcjami.",
"picker_desc_calendar": "Kalendarz kroczący zadań konserwacyjnych z oknami 7/14/30/365 dni, ikonami źródła i znacznikami pewności prognozy.",
"picker_desc_panel": "Pełny panel Maintenance Supporter jako karta — do widoku panelu / podwidoku pulpitu bez wpisu na pasku bocznym.",
"picker_desc_budget": "Edytuj budżet miesięczny i roczny bezpośrednio na pulpicie.",
"picker_desc_groups": "Twórz, zmieniaj nazwy i usuwaj grupy zadań bezpośrednio na pulpicie.",
"picker_desc_vacation": "Włączaj i wyłączaj tryb urlopowy oraz ustawiaj jego daty bezpośrednio na pulpicie.",
"picker_desc_battery_fleet": "Wszystkie śledzone baterie: co jest teraz słabe, co wkrótce się wyczerpie i co kupić.",
"picker_desc_dashboard": "Automatycznie generowany pulpit. Grupuj widoki wg obszaru, statusu, piętra lub terminu — wybór w edytorze strategii lub w YAML.",
"picker_desc_section": "Osadź zadania konserwacyjne (z filtrem wg obszaru, statusu, terminu) jako sekcję w dowolnym widoku pulpitu.",
"picker_desc_vacation_status": "Kompaktowy baner stanu trybu urlopowego. Dotknij, aby otworzyć ustawienia w panelu.",
"picker_desc_budget_status": "Przegląd miesięcznego i rocznego budżetu konserwacji. Dotknij, aby zobaczyć szczegóły.",
"picker_desc_groups_status": "Lista skonfigurowanych grup zadań z liczbą zadań w każdej.",
"hist_note_button": "Wykonano przyciskiem na pulpicie",
"hist_note_todo": "Wykonano z listy zadań",
"hist_note_voice": "Wykonano głosowo",
"hist_note_notification": "Wykonano z powiadomienia",
"hist_note_nfc": "Wykonano tagiem NFC",
"hist_note_shopping_list": "Wykonano z listy zakupów",
"hist_note_todo_mirror": "Wykonano z odzwierciedlonej listy zadań",
"hist_note_skipped_notification": "Pominięto z powiadomienia",
"hist_note_skipped_button": "Pominięto przyciskiem na pulpicie",
"hist_note_sensor_triggered": "Wyzwalacz czujnika aktywowany",
"hist_note_initial": "Wartość początkowa ustawiona przy tworzeniu zadania",
"hist_note_auto_recovered": "Wykonano automatycznie: czujnik wrócił do normy ({value})",
"hist_note_reset": "Zresetowano na {date}",
"hist_note_trigger_replaced": "Zastąpiono encję wyzwalacza: {old} → {new}",
"hist_note_trigger_removed": "Usunięto wyzwalacz czujnika (encja: {entity}). Harmonogram zmieniono na {schedule}.",
"hist_note_compound_removed": "Usunięto encję {entity} z wyzwalacza złożonego; pozostałe warunki: {count}.",
"recent_activities": "Ostatnie aktywności",
"search_notes": "Szukaj w notatkach",
"avg_cost": "Śr. koszt",
@@ -716,6 +795,7 @@
"groups_manage_tasks": "Zarządzaj przypisaniami zadań…",
"groups_open_panel": "Otwórz w panelu",
"on_complete_action_target_hint": "Uwaga: domena encji musi pasować do usługi — np. 'button.press' działa tylko na button.*, 'counter.increment' tylko na counter.*, 'input_button.press' tylko na input_button.* itd. W przypadku niezgodności akcja po cichu się nie wykona (HA zapisze w dzienniku 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Nie, gdy zadanie wykona się samo (jego czujnik wrócił do normy)",
"show_all_objects": "Pokaż wszystkie obiekty",
"show_all_tasks": "Wyczyść filtr — pokaż wszystkie zadania",
"filter_to_overdue": "Filtruj listę tylko do zaległych",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "„{name}”: zadania {before} → {after}",
"adopt_problem_adopt": "Przejmij zaznaczone",
"adopt_problem_done": "Przejęto {tasks} czujnik(ów) problemów",
"adopt_problem_done_one": "Przejęto 1 czujnik problemów",
"views_label": "Widoki",
"views_none": "— Brak widoku —",
"views_manage": "Zapisz / zarządzaj widokami",
@@ -1013,6 +1094,7 @@
"setups_none": "Nie znaleziono obsługiwanych urządzeń z niepodłączonymi czujnikami materiałów.",
"setups_adopt": "Skonfiguruj wybrane",
"setups_done": "Utworzono {tasks} zadań powiązanych z czujnikami.",
"setups_done_one": "Utworzono 1 zadanie powiązane z czujnikami.",
"complete_parts_used": "Części użyte tym razem",
"part_delete_confirm": "Usunąć część '{name}'? Zostaną usunięte śledzenie stanu, powiązania z zadaniami i otwarte przypomnienie o zakupie.",
"baseline_start_value": "Wartość początkowa (opcjonalnie)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "ten sam model na innym urządzeniu",
"setups_maybe_covered": "Prawdopodobnie już istnieje jako „{name}”",
"setups_already": "Już istnieje: {names}",
"setups_reset_line": "Wykonanie resetuje też licznik w integracji („{button}”)",
"setups_reset_enable": "— przycisk zostanie w tym celu włączony",
"reset_offers_title": "Resetuj liczniki po wykonaniu",
"reset_offers_hint": "Te zadania obserwują licznik, który integracja prowadzi sama. Po połączeniu wykonanie zadania tutaj naciska też przycisk resetu integracji — w przeciwnym razie licznik biegnie dalej, a zadanie od razu znów jest do zrobienia.",
"reset_offers_before": "Teraz: po wykonaniu licznik w {integration} liczy dalej",
"reset_offers_after": "Potem: naciska też „{button}”",
"reset_offers_renamed": "Nazwa zmieniona od konfiguracji — sprawdź, czy to zadanie dotyczy tej części",
"reset_offers_apply": "Połącz resety",
"reset_offers_done": "Zadania resetujące licznik po wykonaniu: {count}",
"reset_offers_done_one": "1 zadanie resetuje teraz licznik po wykonaniu",
"setups_before_after": "Zadania obiektu: {before} → {after}",
"setups_new_object_count": "Nowy obiekt „{name}” · zadania: {count}",
"schedule_preview_title": "Najbliższe terminy",
@@ -1046,6 +1138,7 @@
"history_auto": "Automatycznie",
"battery_fleet_title": "Flota baterii",
"battery_fleet_none_low": "Wszystkie baterie OK — nic do wymiany.",
"battery_fleet_progress": "Do wymiany: {n}",
"battery_fleet_buy_now": "Kup teraz",
"battery_fleet_soon": "Wkrótce potrzebne",
"battery_fleet_soon_hint": "Szacowane wg ostatniej wymiany — zamów z wyprzedzeniem.",
@@ -1099,8 +1192,12 @@
"dismiss": "Odrzuć",
"gs_label": "Pierwsze kroki — te wskazówki znikają wraz z rozwojem konfiguracji",
"gs_setups_chip": "Sugerowane konfiguracje: znaleziono {n} urządzeń z gotowymi wyzwalaczami",
"gs_setups_chip_one": "Sugerowane konfiguracje: znaleziono 1 urządzenie z gotowymi wyzwalaczami",
"gs_adopt_chip": "{n} czujników problemów może stać się zadaniami konserwacji",
"gs_adopt_chip_one": "1 czujnik problemów może stać się zadaniem konserwacji",
"gs_fleet_chip": "Jedno kliknięcie konfiguruje flotę baterii",
"gs_reset_chip": "{n} zadań może resetować licznik w integracji",
"gs_reset_chip_one": "1 zadanie może resetować licznik w integracji",
"cal_editor_window": "Okno domyślne",
"cal_editor_window_week": "Tydzień (7 dni)",
"cal_editor_window_fortnight": "Dwa tygodnie (14 dni)",
@@ -243,6 +243,26 @@
"sort_task_name": "Nome da tarefa",
"all_objects": "Todos os objetos",
"all_parts": "Todas as peças",
"all_areas": "Todas as áreas",
"areas_filter": "Filtrar áreas…",
"areas_filter_none": "Nenhuma área corresponde ao filtro.",
"area_cost_year": "Custo {year}",
"area_object_count": "{n} objetos",
"area_object_count_one": "1 objeto",
"area_range_12m": "Últimos 12 meses",
"area_range_all": "Todo o período",
"area_task_filter": "Filtrar por tarefa…",
"area_kpi_completions": "Conclusões",
"area_kpi_total_time": "Tempo total",
"area_cost_per_month": "Custo por mês",
"area_cost_per_year": "Custo por ano",
"area_cost_per_object": "Custo por objeto",
"area_cost_share": "Participação",
"area_history_section": "Histórico (todos os objetos)",
"area_report_print": "Relatório da área (PDF)",
"area_report_title": "Relatório da área",
"area_report_period": "Período",
"area_no_costs": "Nenhum custo registrado neste período.",
"no_tasks_yet": "Ainda sem tarefas",
"add_first_task": "Adicionar a primeira tarefa",
"trigger_configuration": "Configuração do gatilho",
@@ -401,11 +421,70 @@
"qr_download": "Baixar SVG",
"qr_action_view": "Ver informações de manutenção",
"qr_action_complete": "Marcar a manutenção como concluída",
"qr_action_quick_complete": "Conclusão rápida — sem janela",
"qr_print_quick_none": "Os códigos de conclusão rápida precisam de padrões de conclusão rápida em uma tarefa (janela da tarefa → Padrões de conclusão rápida) — nenhuma tarefa dos objetos escolhidos os tem ainda.",
"qr_url_mode": "Tipo de link",
"qr_mode_companion": "Companion App",
"qr_mode_local": "Local (mDNS)",
"qr_mode_server": "URL do servidor",
"overview": "Visão geral",
"strat_this_month": "Este mês",
"strat_later": "Mais tarde",
"strat_week": "Semana",
"strat_fortnight": "Quinzena",
"strat_month": "Mês",
"strat_year": "Ano",
"strat_other": "Outros",
"strat_status": "Situação",
"strat_empty_title": "Ainda não há objetos de manutenção",
"strat_empty_content": "Abra o painel de Manutenção para adicionar seu primeiro objeto — bomba da piscina, filtro do ar-condicionado, veículo, qualquer coisa que precise de cuidados programados.",
"strat_open_panel": "Abrir o painel de Manutenção",
"strat_add_object": "Adicionar objeto",
"strat_not_loaded": "O **Maintenance Supporter** não está carregado. Instale/ative a integração primeiro.",
"strat_group_by": "Agrupar visualizações por",
"strat_group_area": "Por área (padrão)",
"strat_group_status": "Por status (Atrasada / Acionada / Vence em breve / OK)",
"strat_group_floor": "Por andar (usa os andares do HA)",
"strat_group_due_date": "Por vencimento (Atrasada / Hoje / Esta semana / Este mês / Mais tarde)",
"strat_group_calendar": "Calendário contínuo (Semana / Quinzena / Mês / Ano)",
"strat_editor_help": "A visualização \"Visão geral\" é sempre a primeira. Grupos vazios são ignorados.",
"picker_name_calendar": "Maintenance Supporter — Calendário",
"picker_name_panel": "Maintenance Supporter — Painel como cartão",
"picker_name_budget": "Maintenance Supporter — Orçamento de manutenção",
"picker_name_groups": "Maintenance Supporter — Grupos",
"picker_name_vacation": "Maintenance Supporter — Modo férias",
"picker_name_battery_fleet": "Frota de baterias",
"picker_name_section": "Maintenance Supporter — Seção",
"picker_name_vacation_status": "Maintenance Supporter — Status de férias",
"picker_name_budget_status": "Maintenance Supporter — Status do orçamento",
"picker_desc_card": "Visão geral das suas tarefas de manutenção com ações rápidas.",
"picker_desc_calendar": "Calendário contínuo das tarefas de manutenção com janelas de 7/14/30/365 dias, ícones de origem e selos de confiança da previsão.",
"picker_desc_panel": "O painel completo do Maintenance Supporter como cartão — para uma visualização do tipo painel ou uma subvisualização, sem a entrada na barra lateral.",
"picker_desc_budget": "Edite o orçamento mensal e anual direto no painel.",
"picker_desc_groups": "Crie, renomeie e exclua grupos de tarefas direto no painel.",
"picker_desc_vacation": "Ative ou desative o modo férias e defina as datas direto no painel.",
"picker_desc_battery_fleet": "Todas as baterias monitoradas: o que está fraco agora, o que acaba em breve e o que comprar.",
"picker_desc_dashboard": "Painel gerado automaticamente. Agrupe as visualizações por área, status, andar ou vencimento — escolhido no editor da estratégia ou em YAML.",
"picker_desc_section": "Incorpore as tarefas de manutenção (filtráveis por área, status, vencimento) como seção em qualquer visualização do painel.",
"picker_desc_vacation_status": "Banner compacto com o status do modo férias. Toque para abrir as configurações no painel.",
"picker_desc_budget_status": "Visão geral do orçamento de manutenção mensal e anual. Toque para ver os detalhes.",
"picker_desc_groups_status": "Lista dos grupos de tarefas configurados com a quantidade de tarefas de cada um.",
"hist_note_button": "Concluída por um botão do painel",
"hist_note_todo": "Concluída pela lista de tarefas",
"hist_note_voice": "Concluída por voz",
"hist_note_notification": "Concluída pela notificação",
"hist_note_nfc": "Concluída por tag NFC",
"hist_note_shopping_list": "Concluída pela lista de compras",
"hist_note_todo_mirror": "Concluída por uma lista de tarefas espelhada",
"hist_note_skipped_notification": "Pulada pela notificação",
"hist_note_skipped_button": "Pulada por um botão do painel",
"hist_note_sensor_triggered": "Gatilho do sensor acionado",
"hist_note_initial": "Valor inicial definido na criação da tarefa",
"hist_note_auto_recovered": "Concluída automaticamente: o sensor se recuperou ({value})",
"hist_note_reset": "Redefinida para {date}",
"hist_note_trigger_replaced": "Entidade do gatilho substituída: {old} → {new}",
"hist_note_trigger_removed": "Gatilho do sensor removido (entidade era: {entity}). Agendamento convertido para {schedule}.",
"hist_note_compound_removed": "Entidade {entity} removida do gatilho composto; condições restantes: {count}.",
"recent_activities": "Atividades recentes",
"search_notes": "Pesquisar observações",
"avg_cost": "Custo médio",
@@ -709,6 +788,7 @@
"on_complete_action_desc": "Chama um serviço do HA quando a tarefa é concluída — ex.: zerar um contador no dispositivo.",
"on_complete_action_target": "Entidade alvo",
"on_complete_action_target_hint": "Atenção: o domínio da entidade deve corresponder ao serviço — ex.: 'button.press' só funciona em button.*, 'counter.increment' só em counter.*, 'input_button.press' só em input_button.* etc. Em caso de divergência, a ação falhará silenciosamente (o HA registra 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Não quando a tarefa se conclui sozinha (o sensor se recuperou)",
"on_complete_action_data": "Dados (JSON, opcional)",
"on_complete_action_test": "Validar configuração",
"on_complete_action_test_success": "✓ Configuração válida (a ação só disparará na conclusão da tarefa)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "\"{name}\": tarefas {before} → {after}",
"adopt_problem_adopt": "Adotar selecionados",
"adopt_problem_done": "{tasks} sensor(es) de problema adotado(s)",
"adopt_problem_done_one": "1 sensor de problema adotado",
"views_label": "Visualizações",
"views_none": "— Nenhuma visualização —",
"views_manage": "Salvar / gerenciar visualizações",
@@ -1013,6 +1094,7 @@
"setups_none": "Nenhum dispositivo compatível com sensores de consumíveis ainda não conectados foi encontrado.",
"setups_adopt": "Configurar selecionados",
"setups_done": "{tasks} tarefas conectadas a sensores foram criadas.",
"setups_done_one": "1 tarefa conectada a sensores foi criada.",
"complete_parts_used": "Peças usadas desta vez",
"part_delete_confirm": "Excluir a peça '{name}'? O controle de estoque, os vínculos com tarefas e qualquer lembrete de compra aberto serão removidos.",
"baseline_start_value": "Leitura inicial (opcional)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "mesmo modelo em outro dispositivo",
"setups_maybe_covered": "Provavelmente já existe como \"{name}\"",
"setups_already": "Já existe: {names}",
"setups_reset_line": "Concluí-la também zera o contador na integração (“{button}”)",
"setups_reset_enable": "— para isso, o botão será ativado",
"reset_offers_title": "Zerar contadores ao concluir",
"reset_offers_hint": "Estas tarefas acompanham um contador que a própria integração mantém. Depois de conectadas, concluir a tarefa aqui também pressiona o botão de zerar da integração — senão o contador continua correndo e a tarefa vence de novo logo em seguida.",
"reset_offers_before": "Agora: concluí-la deixa o contador em {integration} correndo",
"reset_offers_after": "Depois: também pressiona “{button}”",
"reset_offers_renamed": "Renomeada desde a configuração — confira se esta tarefa é desta peça",
"reset_offers_apply": "Conectar zeragem",
"reset_offers_done": "{count} tarefas agora zeram o contador ao concluir",
"reset_offers_done_one": "1 tarefa agora zera o contador ao concluir",
"setups_before_after": "Tarefas do objeto: {before} → {after}",
"setups_new_object_count": "Novo objeto \"{name}\" · tarefas: {count}",
"schedule_preview_title": "Próximas datas",
@@ -1046,6 +1138,7 @@
"history_auto": "Automática",
"battery_fleet_title": "Frota de baterias",
"battery_fleet_none_low": "Todas as baterias OK — nada a substituir.",
"battery_fleet_progress": "{n} para trocar",
"battery_fleet_buy_now": "Comprar agora",
"battery_fleet_soon": "Necessárias em breve",
"battery_fleet_soon_hint": "Previsto a partir da data da última troca — compre com antecedência.",
@@ -1099,8 +1192,12 @@
"dismiss": "Dispensar",
"gs_label": "Primeiros passos — estas dicas somem conforme a configuração cresce",
"gs_setups_chip": "Configurações sugeridas: {n} dispositivos com gatilhos pré-configurados",
"gs_setups_chip_one": "Configurações sugeridas: 1 dispositivo com gatilhos pré-configurados",
"gs_adopt_chip": "{n} sensores de problema podem virar tarefas de manutenção",
"gs_adopt_chip_one": "1 sensor de problema pode virar uma tarefa de manutenção",
"gs_fleet_chip": "Um clique configura a frota de pilhas",
"gs_reset_chip": "{n} tarefas podem zerar o contador na integração",
"gs_reset_chip_one": "1 tarefa pode zerar o contador na integração",
"cal_editor_window": "Janela padrão",
"cal_editor_window_week": "Semana (7 dias)",
"cal_editor_window_fortnight": "Quinzena (14 dias)",
@@ -241,6 +241,26 @@
"sort_task_name": "Nome da tarefa",
"all_objects": "Todos os objetos",
"all_parts": "Todas as peças",
"all_areas": "Todas as áreas",
"areas_filter": "Filtrar áreas…",
"areas_filter_none": "Nenhuma área corresponde ao filtro.",
"area_cost_year": "Custo {year}",
"area_object_count": "{n} objetos",
"area_object_count_one": "1 objeto",
"area_range_12m": "Últimos 12 meses",
"area_range_all": "Todo o período",
"area_task_filter": "Filtrar por tarefa…",
"area_kpi_completions": "Conclusões",
"area_kpi_total_time": "Tempo total",
"area_cost_per_month": "Custo por mês",
"area_cost_per_year": "Custo por ano",
"area_cost_per_object": "Custo por objeto",
"area_cost_share": "Proporção",
"area_history_section": "Histórico (todos os objetos)",
"area_report_print": "Relatório da área (PDF)",
"area_report_title": "Relatório da área",
"area_report_period": "Período",
"area_no_costs": "Sem custos registados neste período.",
"no_tasks_yet": "Ainda sem tarefas",
"add_first_task": "Adicionar primeira tarefa",
"trigger_configuration": "Configuração do acionador",
@@ -399,11 +419,70 @@
"qr_download": "Transferir SVG",
"qr_action_view": "Ver informações de manutenção",
"qr_action_complete": "Marcar manutenção como concluída",
"qr_action_quick_complete": "Conclusão rápida — sem janela",
"qr_print_quick_none": "Os códigos de conclusão rápida precisam de valores predefinidos de conclusão rápida numa tarefa (janela da tarefa → Valores predefinidos de conclusão rápida) — nenhuma tarefa dos objetos escolhidos os tem ainda.",
"qr_url_mode": "Tipo de ligação",
"qr_mode_companion": "Companion App",
"qr_mode_local": "Local (mDNS)",
"qr_mode_server": "URL do servidor",
"overview": "Visão geral",
"strat_this_month": "Este mês",
"strat_later": "Mais tarde",
"strat_week": "Semana",
"strat_fortnight": "Quinzena",
"strat_month": "Mês",
"strat_year": "Ano",
"strat_other": "Outros",
"strat_status": "Estado",
"strat_empty_title": "Ainda sem objetos de manutenção",
"strat_empty_content": "Abra o painel Manutenção para adicionar o seu primeiro objeto — bomba da piscina, filtro de climatização, veículo, tudo o que precise de cuidados programados.",
"strat_open_panel": "Abrir o painel Manutenção",
"strat_add_object": "Adicionar objeto",
"strat_not_loaded": "O **Maintenance Supporter** não está carregado. Instale/ative primeiro a integração.",
"strat_group_by": "Agrupar vistas por",
"strat_group_area": "Por área (predefinido)",
"strat_group_status": "Por estado (Atrasada / Acionada / Próxima / OK)",
"strat_group_floor": "Por piso (usa os pisos do HA)",
"strat_group_due_date": "Por vencimento (Atrasada / Hoje / Esta semana / Este mês / Mais tarde)",
"strat_group_calendar": "Calendário deslizante (Semana / Quinzena / Mês / Ano)",
"strat_editor_help": "A vista «Visão geral» é sempre a primeira. Os grupos vazios são ignorados.",
"picker_name_calendar": "Maintenance Supporter — Calendário",
"picker_name_panel": "Maintenance Supporter — Painel como cartão",
"picker_name_budget": "Maintenance Supporter — Orçamento de manutenção",
"picker_name_groups": "Maintenance Supporter — Grupos",
"picker_name_vacation": "Maintenance Supporter — Modo de férias",
"picker_name_battery_fleet": "Frota de baterias",
"picker_name_section": "Maintenance Supporter — Secção",
"picker_name_vacation_status": "Maintenance Supporter — Estado das férias",
"picker_name_budget_status": "Maintenance Supporter — Estado do orçamento",
"picker_desc_card": "Visão geral das suas tarefas de manutenção com ações rápidas.",
"picker_desc_calendar": "Calendário deslizante das tarefas de manutenção com janelas de 7/14/30/365 dias, ícones de origem e etiquetas de confiança da previsão.",
"picker_desc_panel": "O painel completo do Maintenance Supporter como cartão — para uma vista do tipo painel ou uma subvista, sem entrada na barra lateral.",
"picker_desc_budget": "Edite o orçamento mensal e anual diretamente no painel.",
"picker_desc_groups": "Crie, mude o nome e elimine grupos de tarefas diretamente no painel.",
"picker_desc_vacation": "Ative ou desative o modo de férias e defina as datas diretamente no painel.",
"picker_desc_battery_fleet": "Todas as pilhas monitorizadas: o que está fraco agora, o que vai acabar em breve e o que comprar.",
"picker_desc_dashboard": "Painel gerado automaticamente. Agrupe as vistas por área, estado, piso ou vencimento — a escolher no editor da estratégia ou em YAML.",
"picker_desc_section": "Incorpore as tarefas de manutenção (filtráveis por área, estado, vencimento) como secção em qualquer vista do painel.",
"picker_desc_vacation_status": "Faixa compacta com o estado do modo de férias. Toque para abrir as definições no painel.",
"picker_desc_budget_status": "Visão geral do orçamento de manutenção mensal e anual. Toque para ver detalhes.",
"picker_desc_groups_status": "Lista dos grupos de tarefas configurados com o número de tarefas de cada um.",
"hist_note_button": "Concluída através de um botão do painel",
"hist_note_todo": "Concluída a partir da lista de tarefas",
"hist_note_voice": "Concluída por voz",
"hist_note_notification": "Concluída a partir da notificação",
"hist_note_nfc": "Concluída com uma etiqueta NFC",
"hist_note_shopping_list": "Concluída a partir da lista de compras",
"hist_note_todo_mirror": "Concluída a partir de uma lista de tarefas espelhada",
"hist_note_skipped_notification": "Saltada a partir da notificação",
"hist_note_skipped_button": "Saltada através de um botão do painel",
"hist_note_sensor_triggered": "Gatilho do sensor acionado",
"hist_note_initial": "Valor inicial definido ao criar a tarefa",
"hist_note_auto_recovered": "Concluída automaticamente: sensor recuperado ({value})",
"hist_note_reset": "Reposta para {date}",
"hist_note_trigger_replaced": "Entidade do gatilho substituída: {old} → {new}",
"hist_note_trigger_removed": "Gatilho de sensor removido (entidade: {entity}). Agenda convertida para {schedule}.",
"hist_note_compound_removed": "Entidade {entity} removida do gatilho composto; condições restantes: {count}.",
"recent_activities": "Atividades recentes",
"search_notes": "Pesquisar notas",
"avg_cost": "Ø Custo",
@@ -716,6 +795,7 @@
"groups_manage_tasks": "Gerir atribuições de tarefas…",
"groups_open_panel": "Abrir no painel",
"on_complete_action_target_hint": "Nota: o domínio da entidade tem de corresponder ao serviço — p. ex. 'button.press' só funciona em button.*, 'counter.increment' só em counter.*, 'input_button.press' só em input_button.* etc. Em caso de incompatibilidade, a ação falha silenciosamente (o HA regista 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Não quando a tarefa se conclui sozinha (o sensor recuperou)",
"show_all_objects": "Mostrar todos os objetos",
"show_all_tasks": "Limpar filtro — mostrar todas as tarefas",
"filter_to_overdue": "Filtrar a lista apenas para em atraso",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "«{name}»: tarefas {before} → {after}",
"adopt_problem_adopt": "Adotar selecionados",
"adopt_problem_done": "{tasks} sensor(es) de problema adotado(s)",
"adopt_problem_done_one": "1 sensor de problema adotado",
"views_label": "Vistas",
"views_none": "— Sem vista —",
"views_manage": "Guardar / gerir vistas",
@@ -1013,6 +1094,7 @@
"setups_none": "Nenhum dispositivo suportado com sensores de consumíveis por ligar.",
"setups_adopt": "Configurar seleção",
"setups_done": "{tasks} tarefas ligadas a sensores criadas.",
"setups_done_one": "1 tarefa ligada a sensores criada.",
"complete_parts_used": "Peças usadas desta vez",
"part_delete_confirm": "Eliminar a peça '{name}'? O controlo de stock, as ligações a tarefas e qualquer lembrete de compra aberto serão removidos.",
"baseline_start_value": "Valor inicial (opcional)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "mesmo modelo noutro dispositivo",
"setups_maybe_covered": "Provavelmente já existe como «{name}»",
"setups_already": "Já existe: {names}",
"setups_reset_line": "Concluí-la também reinicia o contador na integração («{button}»)",
"setups_reset_enable": "— para isso, o botão será ativado",
"reset_offers_title": "Reiniciar contadores na conclusão",
"reset_offers_hint": "Estas tarefas acompanham um contador que a própria integração mantém. Uma vez ligadas, concluir a tarefa aqui também prime o botão de reinício da integração — caso contrário, o contador continua a contar e a tarefa volta logo a vencer.",
"reset_offers_before": "Agora: concluí-la deixa o contador em {integration} a contar",
"reset_offers_after": "Depois: também prime «{button}»",
"reset_offers_renamed": "Renomeada desde a configuração — confirme que esta tarefa corresponde a esta peça",
"reset_offers_apply": "Ligar reinícios",
"reset_offers_done": "{count} tarefas passam a reiniciar o contador na conclusão",
"reset_offers_done_one": "1 tarefa passa a reiniciar o contador na conclusão",
"setups_before_after": "Tarefas do objeto: {before} → {after}",
"setups_new_object_count": "Novo objeto «{name}» · tarefas: {count}",
"schedule_preview_title": "Próximas datas",
@@ -1046,6 +1138,7 @@
"history_auto": "Automático",
"battery_fleet_title": "Frota de baterias",
"battery_fleet_none_low": "Todas as baterias OK — nada a substituir.",
"battery_fleet_progress": "{n} a substituir",
"battery_fleet_buy_now": "Comprar agora",
"battery_fleet_soon": "Necessário em breve",
"battery_fleet_soon_hint": "Estimado pela última substituição — encomende com antecedência.",
@@ -1099,8 +1192,12 @@
"dismiss": "Dispensar",
"gs_label": "Primeiros passos — estas dicas desaparecem à medida que a configuração cresce",
"gs_setups_chip": "Configurações sugeridas: {n} dispositivos com gatilhos pré-configurados",
"gs_setups_chip_one": "Configurações sugeridas: 1 dispositivo com gatilhos pré-configurados",
"gs_adopt_chip": "{n} sensores de problema podem tornar-se tarefas de manutenção",
"gs_adopt_chip_one": "1 sensor de problema pode tornar-se uma tarefa de manutenção",
"gs_fleet_chip": "Um clique configura a frota de pilhas",
"gs_reset_chip": "{n} tarefas podem reiniciar o contador na integração",
"gs_reset_chip_one": "1 tarefa pode reiniciar o contador na integração",
"cal_editor_window": "Janela predefinida",
"cal_editor_window_week": "Semana (7 dias)",
"cal_editor_window_fortnight": "Quinzena (14 dias)",
@@ -240,6 +240,26 @@
"sort_task_name": "Имя задачи",
"all_objects": "Все объекты",
"all_parts": "Все детали",
"all_areas": "Все зоны",
"areas_filter": "Фильтр зон…",
"areas_filter_none": "Нет зон, соответствующих фильтру.",
"area_cost_year": "Стоимость за {year}",
"area_object_count": "Объектов: {n}",
"area_object_count_one": "1 объект",
"area_range_12m": "Последние 12 месяцев",
"area_range_all": "За всё время",
"area_task_filter": "Фильтр по задаче…",
"area_kpi_completions": "Выполнения",
"area_kpi_total_time": "Общее время",
"area_cost_per_month": "Стоимость по месяцам",
"area_cost_per_year": "Стоимость по годам",
"area_cost_per_object": "Стоимость по объектам",
"area_cost_share": "Доля",
"area_history_section": "История (все объекты)",
"area_report_print": "Отчёт по зоне (PDF)",
"area_report_title": "Отчёт по зоне",
"area_report_period": "Период",
"area_no_costs": "За этот период затраты не записаны.",
"no_tasks_yet": "Пока нет задач",
"add_first_task": "Добавить первую задачу",
"last_performed_optional": "Последнее выполнение (опционально)",
@@ -399,11 +419,70 @@
"qr_download": "Скачать SVG",
"qr_action_view": "Просмотр",
"qr_action_complete": "Отметить обслуживание как выполненное",
"qr_action_quick_complete": "Быстрое завершение — без диалога",
"qr_print_quick_none": "Для кодов быстрого завершения у задачи должны быть значения по умолчанию для быстрого завершения (окно задачи → Значения по умолчанию для быстрого завершения) — ни у одной задачи выбранных объектов их пока нет.",
"qr_url_mode": "Тип ссылки",
"qr_mode_companion": "Приложение-компаньон",
"qr_mode_local": "Локальный (mDNS)",
"qr_mode_server": "URL сервера",
"overview": "Обзор",
"strat_this_month": "В этом месяце",
"strat_later": "Позже",
"strat_week": "Неделя",
"strat_fortnight": "Две недели",
"strat_month": "Месяц",
"strat_year": "Год",
"strat_other": "Прочее",
"strat_status": "Статус",
"strat_empty_title": "Пока нет объектов обслуживания",
"strat_empty_content": "Откройте панель обслуживания, чтобы добавить первый объект — насос бассейна, фильтр вентиляции, автомобиль, всё, что требует планового ухода.",
"strat_open_panel": "Открыть панель обслуживания",
"strat_add_object": "Добавить объект",
"strat_not_loaded": "**Maintenance Supporter** не загружен. Сначала установите/включите интеграцию.",
"strat_group_by": "Группировать вкладки по",
"strat_group_area": "По зонам (по умолчанию)",
"strat_group_status": "По статусу (Просрочено / Сработало / Скоро / OK)",
"strat_group_floor": "По этажам (этажи из HA)",
"strat_group_due_date": "По сроку (Просрочено / Сегодня / На этой неделе / В этом месяце / Позже)",
"strat_group_calendar": "Скользящий календарь (Неделя / Две недели / Месяц / Год)",
"strat_editor_help": "Вкладка «Обзор» всегда первая. Пустые группы пропускаются.",
"picker_name_calendar": "Maintenance Supporter — Календарь",
"picker_name_panel": "Maintenance Supporter — Панель в карточке",
"picker_name_budget": "Maintenance Supporter — Бюджет обслуживания",
"picker_name_groups": "Maintenance Supporter — Группы",
"picker_name_vacation": "Maintenance Supporter — Режим отпуска",
"picker_name_battery_fleet": "Парк батарей",
"picker_name_section": "Maintenance Supporter — Раздел",
"picker_name_vacation_status": "Maintenance Supporter — Статус отпуска",
"picker_name_budget_status": "Maintenance Supporter — Статус бюджета",
"picker_desc_card": "Обзор задач обслуживания с быстрыми действиями.",
"picker_desc_calendar": "Скользящий календарь задач обслуживания с окнами 7/14/30/365 дней, значками источника и метками надёжности прогноза.",
"picker_desc_panel": "Вся панель Maintenance Supporter в виде карточки — для вкладки в режиме «Панель» или подвкладки без пункта в боковом меню.",
"picker_desc_budget": "Редактируйте месячный и годовой бюджет прямо на панели.",
"picker_desc_groups": "Создавайте, переименовывайте и удаляйте группы задач прямо на панели.",
"picker_desc_vacation": "Включайте и выключайте режим отпуска и задавайте его даты прямо на панели.",
"picker_desc_battery_fleet": "Все отслеживаемые батареи: какие разряжены сейчас, какие скоро сядут и что купить.",
"picker_desc_dashboard": "Автоматически создаваемая панель. Группировка вкладок по зоне, статусу, этажу или сроку — выбор в редакторе стратегии или в YAML.",
"picker_desc_section": "Встраивайте задачи обслуживания (с фильтром по зоне, статусу, сроку) как раздел в любую вкладку панели.",
"picker_desc_vacation_status": "Компактный баннер статуса режима отпуска. Нажмите, чтобы открыть настройки на панели.",
"picker_desc_budget_status": "Обзор месячного и годового бюджета на обслуживание. Нажмите для подробностей.",
"picker_desc_groups_status": "Список настроенных групп задач с числом задач в каждой.",
"hist_note_button": "Выполнено кнопкой на панели",
"hist_note_todo": "Выполнено из списка дел",
"hist_note_voice": "Выполнено голосом",
"hist_note_notification": "Выполнено из уведомления",
"hist_note_nfc": "Выполнено через NFC-метку",
"hist_note_shopping_list": "Выполнено из списка покупок",
"hist_note_todo_mirror": "Выполнено из отражённого списка дел",
"hist_note_skipped_notification": "Пропущено из уведомления",
"hist_note_skipped_button": "Пропущено кнопкой на панели",
"hist_note_sensor_triggered": "Сработал триггер датчика",
"hist_note_initial": "Начальное значение задано при создании задачи",
"hist_note_auto_recovered": "Автоматически выполнено: датчик восстановился ({value})",
"hist_note_reset": "Сброшено на {date}",
"hist_note_trigger_replaced": "Сущность триггера заменена: {old} → {new}",
"hist_note_trigger_removed": "Триггер датчика удалён (сущность: {entity}). Расписание изменено на «{schedule}».",
"hist_note_compound_removed": "Сущность {entity} удалена из составного триггера; осталось условий: {count}.",
"recent_activities": "Недавние действия",
"search_notes": "Поиск по заметкам",
"avg_cost": "Ср. стоимость",
@@ -716,6 +795,7 @@
"groups_manage_tasks": "Управление назначениями задач…",
"groups_open_panel": "Открыть на панели",
"on_complete_action_target_hint": "Примечание: домен сущности должен соответствовать сервису — напр. 'button.press' работает только с button.*, 'counter.increment' только с counter.*, 'input_button.press' только с input_button.* и т. д. При несоответствии действие молча не выполнится (HA запишет в журнал 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Не когда задача выполняется сама (датчик восстановился)",
"show_all_objects": "Показать все объекты",
"show_all_tasks": "Сбросить фильтр — показать все задачи",
"filter_to_overdue": "Фильтровать список только по просроченным",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "«{name}»: задачи {before} → {after}",
"adopt_problem_adopt": "Принять выбранные",
"adopt_problem_done": "Принято сенсоров проблем: {tasks}",
"adopt_problem_done_one": "Принят 1 сенсор проблем",
"views_label": "Представления",
"views_none": "— Без представления —",
"views_manage": "Сохранить / управлять представлениями",
@@ -1013,6 +1094,7 @@
"setups_none": "Поддерживаемых устройств с непривязанными датчиками расходников не найдено.",
"setups_adopt": "Настроить выбранное",
"setups_done": "Создано задач с привязкой к датчикам: {tasks}.",
"setups_done_one": "Создана 1 задача с привязкой к датчикам.",
"complete_parts_used": "Использованные в этот раз запчасти",
"part_delete_confirm": "Удалить запчасть '{name}'? Будут удалены учёт остатков, связи с задачами и открытое напоминание о покупке.",
"baseline_start_value": "Начальное значение (необязательно)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "та же модель на другом устройстве",
"setups_maybe_covered": "Вероятно, уже есть как «{name}»",
"setups_already": "Уже есть: {names}",
"setups_reset_line": "Выполнение также сбрасывает счётчик в интеграции («{button}»)",
"setups_reset_enable": "— для этого кнопка будет включена",
"reset_offers_title": "Сбрасывать счётчики при выполнении",
"reset_offers_hint": "Эти задачи следят за счётчиком, который интеграция ведёт сама. После привязки выполнение задачи здесь также нажимает кнопку сброса интеграции — иначе счётчик продолжает идти и срок задачи сразу наступает снова.",
"reset_offers_before": "Сейчас: при выполнении счётчик в {integration} продолжает идти",
"reset_offers_after": "Потом: также нажимается «{button}»",
"reset_offers_renamed": "Переименована после настройки — проверьте, что задача относится к этой детали",
"reset_offers_apply": "Привязать сброс",
"reset_offers_done": "Задач со сбросом счётчика при выполнении: {count}",
"reset_offers_done_one": "1 задача теперь сбрасывает счётчик при выполнении",
"setups_before_after": "Задачи объекта: {before} → {after}",
"setups_new_object_count": "Новый объект «{name}» · задачи: {count}",
"schedule_preview_title": "Ближайшие даты",
@@ -1046,6 +1138,7 @@
"history_auto": "Автоматически",
"battery_fleet_title": "Парк батарей",
"battery_fleet_none_low": "Все батареи в порядке — менять нечего.",
"battery_fleet_progress": "К замене: {n}",
"battery_fleet_buy_now": "Купить сейчас",
"battery_fleet_soon": "Скоро понадобится",
"battery_fleet_soon_hint": "Оценка по дате последней замены — закажите заранее.",
@@ -1099,8 +1192,12 @@
"dismiss": "Скрыть",
"gs_label": "Первые шаги — эти подсказки исчезнут по мере роста настройки",
"gs_setups_chip": "Рекомендуемые настройки: найдено {n} устройств с готовыми триггерами",
"gs_setups_chip_one": "Рекомендуемые настройки: найдено 1 устройство с готовыми триггерами",
"gs_adopt_chip": "{n} датчиков проблем могут стать задачами обслуживания",
"gs_adopt_chip_one": "1 датчик проблем может стать задачей обслуживания",
"gs_fleet_chip": "Один клик настроит парк батарей",
"gs_reset_chip": "Сброс счётчика в интеграции доступен для задач: {n}",
"gs_reset_chip_one": "Сброс счётчика в интеграции доступен для 1 задачи",
"cal_editor_window": "Окно по умолчанию",
"cal_editor_window_week": "Неделя (7 дней)",
"cal_editor_window_fortnight": "Две недели (14 дней)",
@@ -241,6 +241,26 @@
"sort_task_name": "Uppgiftsnamn",
"all_objects": "Alla objekt",
"all_parts": "Alla delar",
"all_areas": "Alla områden",
"areas_filter": "Filtrera områden…",
"areas_filter_none": "Inget område matchar filtret.",
"area_cost_year": "Kostnad {year}",
"area_object_count": "{n} objekt",
"area_object_count_one": "1 objekt",
"area_range_12m": "Senaste 12 månaderna",
"area_range_all": "Hela perioden",
"area_task_filter": "Filtrera på uppgift…",
"area_kpi_completions": "Utföranden",
"area_kpi_total_time": "Total tid",
"area_cost_per_month": "Kostnad per månad",
"area_cost_per_year": "Kostnad per år",
"area_cost_per_object": "Kostnad per objekt",
"area_cost_share": "Andel",
"area_history_section": "Historik (alla objekt)",
"area_report_print": "Områdesrapport (PDF)",
"area_report_title": "Områdesrapport",
"area_report_period": "Tidsperiod",
"area_no_costs": "Inga kostnader registrerade under denna period.",
"no_tasks_yet": "Inga uppgifter ännu",
"add_first_task": "Lägg till första uppgift",
"trigger_configuration": "Utlösarkonfiguration",
@@ -399,11 +419,70 @@
"qr_download": "Ladda ner SVG",
"qr_action_view": "Visa underhållsinformation",
"qr_action_complete": "Markera underhåll som slutfört",
"qr_action_quick_complete": "Snabb-slutför — utan dialogruta",
"qr_print_quick_none": "Snabb-slutför-koder kräver snabb-slutför standardvärden på en uppgift (uppgiftsdialogen → Snabb-slutför standardvärden) — ingen uppgift i de valda objekten har några ännu.",
"qr_url_mode": "Länktyp",
"qr_mode_companion": "Companion App",
"qr_mode_local": "Lokal (mDNS)",
"qr_mode_server": "Server-URL",
"overview": "Översikt",
"strat_this_month": "Den här månaden",
"strat_later": "Senare",
"strat_week": "Vecka",
"strat_fortnight": "Två veckor",
"strat_month": "Månad",
"strat_year": "År",
"strat_other": "Övrigt",
"strat_status": "Aktuellt läge",
"strat_empty_title": "Inga underhållsobjekt än",
"strat_empty_content": "Öppna Underhållspanelen för att lägga till ditt första objekt — poolpump, ventilationsfilter, fordon, allt som behöver schemalagd skötsel.",
"strat_open_panel": "Öppna Underhållspanelen",
"strat_add_object": "Lägg till objekt",
"strat_not_loaded": "**Maintenance Supporter** är inte laddad. Installera/aktivera integrationen först.",
"strat_group_by": "Gruppera vyer efter",
"strat_group_area": "Per område (standard)",
"strat_group_status": "Efter status (Försenad / Utlöst / Snart / OK)",
"strat_group_floor": "Per våning (använder HA-våningarna)",
"strat_group_due_date": "Efter förfallodatum (Försenad / Idag / Den här veckan / Den här månaden / Senare)",
"strat_group_calendar": "Rullande kalender (Vecka / Två veckor / Månad / År)",
"strat_editor_help": "Vyn ”Översikt” visas alltid först. Tomma grupper hoppas över.",
"picker_name_calendar": "Maintenance Supporter — Kalender",
"picker_name_panel": "Maintenance Supporter — Panel som kort",
"picker_name_budget": "Maintenance Supporter — Underhållsbudget",
"picker_name_groups": "Maintenance Supporter — Grupper",
"picker_name_vacation": "Maintenance Supporter — Semesterläge",
"picker_name_battery_fleet": "Batteriflotta",
"picker_name_section": "Maintenance Supporter — Sektion",
"picker_name_vacation_status": "Maintenance Supporter — Semesterstatus",
"picker_name_budget_status": "Maintenance Supporter — Budgetstatus",
"picker_desc_card": "Översikt över dina underhållsuppgifter med snabbåtgärder.",
"picker_desc_calendar": "Rullande kalender över underhållsuppgifter med fönster på 7/14/30/365 dagar, källikoner och etiketter för prognossäkerhet.",
"picker_desc_panel": "Hela Maintenance Supporter-panelen som kort — för en panelvy / undervy på en instrumentpanel utan post i sidofältet.",
"picker_desc_budget": "Redigera månads- och årsbudgeten direkt på instrumentpanelen.",
"picker_desc_groups": "Skapa, byt namn på och ta bort uppgiftsgrupper direkt på instrumentpanelen.",
"picker_desc_vacation": "Slå på eller av semesterläget och ange datum direkt på instrumentpanelen.",
"picker_desc_battery_fleet": "Alla bevakade batterier: vad som är svagt nu, vad som snart tar slut och vad du behöver köpa.",
"picker_desc_dashboard": "Automatiskt genererad instrumentpanel. Gruppera vyer efter område, status, våning eller förfallodatum — väljs i strategiredigeraren eller i YAML.",
"picker_desc_section": "Bädda in underhållsuppgifter (filtrerbara efter område, status, förfallodatum) som en sektion i valfri vy på instrumentpanelen.",
"picker_desc_vacation_status": "Kompakt statusbanner för semesterläget. Tryck för att öppna inställningarna i panelen.",
"picker_desc_budget_status": "Översikt över månads- och årsbudgeten för underhåll. Tryck för detaljer.",
"picker_desc_groups_status": "Lista över konfigurerade uppgiftsgrupper med antal uppgifter i varje.",
"hist_note_button": "Slutförd via knapp på instrumentpanelen",
"hist_note_todo": "Slutförd från att-göra-listan",
"hist_note_voice": "Slutförd med rösten",
"hist_note_notification": "Slutförd från notifikationen",
"hist_note_nfc": "Slutförd via NFC-tagg",
"hist_note_shopping_list": "Slutförd från inköpslistan",
"hist_note_todo_mirror": "Slutförd från en speglad att-göra-lista",
"hist_note_skipped_notification": "Hoppades över från notifikationen",
"hist_note_skipped_button": "Hoppades över via knapp på instrumentpanelen",
"hist_note_sensor_triggered": "Sensorutlösare aktiverad",
"hist_note_initial": "Startvärde angavs när uppgiften skapades",
"hist_note_auto_recovered": "Automatiskt slutförd: sensorn återhämtade sig ({value})",
"hist_note_reset": "Återställd till {date}",
"hist_note_trigger_replaced": "Utlösarentitet ersatt: {old} → {new}",
"hist_note_trigger_removed": "Sensorutlösare borttagen (entitet: {entity}). Schemat ändrat till {schedule}.",
"hist_note_compound_removed": "Entiteten {entity} togs bort från den sammansatta utlösaren; återstående villkor: {count}.",
"recent_activities": "Senaste aktiviteter",
"search_notes": "Sök i anteckningar",
"avg_cost": "Snittlig kostnad",
@@ -716,6 +795,7 @@
"groups_manage_tasks": "Hantera uppgiftstilldelningar…",
"groups_open_panel": "Öppna i panelen",
"on_complete_action_target_hint": "Obs: entitetens domän måste matcha tjänsten — t.ex. 'button.press' fungerar bara på button.*, 'counter.increment' bara på counter.*, 'input_button.press' bara på input_button.* osv. Vid en avvikelse misslyckas åtgärden tyst (HA loggar 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Inte när uppgiften slutförs av sig själv (sensorn har återhämtat sig)",
"show_all_objects": "Visa alla objekt",
"show_all_tasks": "Rensa filter — visa alla uppgifter",
"filter_to_overdue": "Filtrera listan till endast försenade",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "”{name}”: uppgifter {before} → {after}",
"adopt_problem_adopt": "Anta valda",
"adopt_problem_done": "Antog {tasks} problemsensor(er)",
"adopt_problem_done_one": "Antog 1 problemsensor",
"views_label": "Vyer",
"views_none": "— Ingen vy —",
"views_manage": "Spara / hantera vyer",
@@ -1013,6 +1094,7 @@
"setups_none": "Inga enheter med okopplade förbrukningssensorer hittades.",
"setups_adopt": "Konfigurera valda",
"setups_done": "{tasks} sensorkopplade uppgifter skapade.",
"setups_done_one": "1 sensorkopplad uppgift skapad.",
"complete_parts_used": "Delar använda denna gång",
"part_delete_confirm": "Ta bort delen '{name}'? Lagerspårning, uppgiftskopplingar och en öppen köppåminnelse tas bort.",
"baseline_start_value": "Startvärde (valfritt)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "samma modell på en annan enhet",
"setups_maybe_covered": "Finns troligen redan som ”{name}”",
"setups_already": "Finns redan: {names}",
"setups_reset_line": "Att slutföra den återställer också räknaren i integrationen (”{button}”)",
"setups_reset_enable": "— knappen slås på för detta",
"reset_offers_title": "Återställ räknare vid slutförande",
"reset_offers_hint": "Dessa uppgifter bevakar en räknare som integrationen själv håller. När de är kopplade trycker ett slutförande här också på integrationens återställningsknapp — annars fortsätter räknaren att gå och uppgiften förfaller direkt igen.",
"reset_offers_before": "Nu: att slutföra den låter räknaren i {integration} fortsätta",
"reset_offers_after": "Sedan: trycker också på ”{button}”",
"reset_offers_renamed": "Omdöpt sedan konfigurationen — kontrollera att uppgiften gäller den här delen",
"reset_offers_apply": "Koppla återställningar",
"reset_offers_done": "{count} uppgifter återställer nu sin räknare vid slutförande",
"reset_offers_done_one": "1 uppgift återställer nu sin räknare vid slutförande",
"setups_before_after": "Uppgifter på objektet: {before} → {after}",
"setups_new_object_count": "Nytt objekt ”{name}” · uppgifter: {count}",
"schedule_preview_title": "Kommande datum",
@@ -1046,6 +1138,7 @@
"history_auto": "Automatiskt",
"battery_fleet_title": "Batteriflotta",
"battery_fleet_none_low": "Alla batterier OK — inget att byta.",
"battery_fleet_progress": "{n} att byta",
"battery_fleet_buy_now": "Köp nu",
"battery_fleet_soon": "Behövs snart",
"battery_fleet_soon_hint": "Uppskattat från senaste bytet — beställ i förväg.",
@@ -1099,8 +1192,12 @@
"dismiss": "Avfärda",
"gs_label": "Kom igång — tipsen försvinner när din installation växer",
"gs_setups_chip": "Föreslagna uppsättningar hittade {n} enheter med förkopplade utlösare",
"gs_setups_chip_one": "Föreslagna uppsättningar hittade 1 enhet med förkopplade utlösare",
"gs_adopt_chip": "{n} problemsensorer kan bli underhållsuppgifter",
"gs_adopt_chip_one": "1 problemsensor kan bli en underhållsuppgift",
"gs_fleet_chip": "Ett klick konfigurerar batteriflottan",
"gs_reset_chip": "{n} uppgifter kan återställa sin räknare i integrationen",
"gs_reset_chip_one": "1 uppgift kan återställa sin räknare i integrationen",
"cal_editor_window": "Standardfönster",
"cal_editor_window_week": "Vecka (7 dagar)",
"cal_editor_window_fortnight": "Två veckor (14 dagar)",
@@ -243,6 +243,26 @@
"sort_task_name": "Görev adı",
"all_objects": "Tüm nesneler",
"all_parts": "Tüm parçalar",
"all_areas": "Tüm alanlar",
"areas_filter": "Alanları filtrele…",
"areas_filter_none": "Filtreyle eşleşen alan yok.",
"area_cost_year": "{year} maliyeti",
"area_object_count": "{n} nesne",
"area_object_count_one": "1 nesne",
"area_range_12m": "Son 12 ay",
"area_range_all": "Tüm zamanlar",
"area_task_filter": "Göreve göre filtrele…",
"area_kpi_completions": "Tamamlamalar",
"area_kpi_total_time": "Toplam süre",
"area_cost_per_month": "Aylık maliyet",
"area_cost_per_year": "Yıllık maliyet",
"area_cost_per_object": "Nesne başına maliyet",
"area_cost_share": "Pay",
"area_history_section": "Geçmiş (tüm nesneler)",
"area_report_print": "Alan raporu (PDF)",
"area_report_title": "Alan raporu",
"area_report_period": "Dönem",
"area_no_costs": "Bu dönemde kaydedilmiş maliyet yok.",
"no_tasks_yet": "Henüz görev yok",
"add_first_task": "İlk görevi ekle",
"trigger_configuration": "Tetikleyici Yapılandırması",
@@ -401,11 +421,70 @@
"qr_download": "SVG indir",
"qr_action_view": "Bakım bilgisini görüntüle",
"qr_action_complete": "Bakımı tamamlandı olarak işaretle",
"qr_action_quick_complete": "Hızlı tamamlama — iletişim kutusu olmadan",
"qr_print_quick_none": "Hızlı tamamlama kodları için bir görevde hızlı tamamlama varsayılanları gerekir (görev penceresi → Hızlı tamamlama varsayılanları) — seçilen nesnelerin hiçbir görevinde henüz yok.",
"qr_url_mode": "Bağlantı türü",
"qr_mode_companion": "Companion uygulaması",
"qr_mode_local": "Yerel (mDNS)",
"qr_mode_server": "Sunucu URL'si",
"overview": "Genel Bakış",
"strat_this_month": "Bu ay",
"strat_later": "Daha sonra",
"strat_week": "Hafta",
"strat_fortnight": "İki hafta",
"strat_month": "Ay",
"strat_year": "Yıl",
"strat_other": "Diğer",
"strat_status": "Durum",
"strat_empty_title": "Henüz bakım nesnesi yok",
"strat_empty_content": "İlk nesnenizi eklemek için Bakım panelini açın — havuz pompası, klima filtresi, araç, düzenli bakım gerektiren her şey.",
"strat_open_panel": "Bakım panelini aç",
"strat_add_object": "Nesne ekle",
"strat_not_loaded": "**Maintenance Supporter** yüklenmedi. Önce entegrasyonu kurun/etkinleştirin.",
"strat_group_by": "Görünümleri grupla",
"strat_group_area": "Alana göre (varsayılan)",
"strat_group_status": "Duruma göre (Gecikmiş / Tetiklendi / Yaklaşan / Tamam)",
"strat_group_floor": "Kata göre (HA katlarını kullanır)",
"strat_group_due_date": "Vadeye göre (Gecikmiş / Bugün / Bu hafta / Bu ay / Daha sonra)",
"strat_group_calendar": "Kayan takvim (Hafta / İki hafta / Ay / Yıl)",
"strat_editor_help": "\"Genel Bakış\" görünümü her zaman ilk sıradadır. Boş gruplar atlanır.",
"picker_name_calendar": "Maintenance Supporter — Takvim",
"picker_name_panel": "Maintenance Supporter — Panel kartı",
"picker_name_budget": "Maintenance Supporter — Bakım bütçesi",
"picker_name_groups": "Maintenance Supporter — Gruplar",
"picker_name_vacation": "Maintenance Supporter — Tatil modu",
"picker_name_battery_fleet": "Pil filosu",
"picker_name_section": "Maintenance Supporter — Bölüm",
"picker_name_vacation_status": "Maintenance Supporter — Tatil durumu",
"picker_name_budget_status": "Maintenance Supporter — Bütçe durumu",
"picker_desc_card": "Hızlı eylemlerle bakım görevlerinize genel bakış.",
"picker_desc_calendar": "7/14/30/365 günlük pencereler, kaynak simgeleri ve tahmin güveni etiketleriyle bakım görevlerinin kayan takvimi.",
"picker_desc_panel": "Maintenance Supporter panelinin tamamı kart olarak — kenar çubuğu girişi olmadan bir panel görünümü / pano alt görünümü için.",
"picker_desc_budget": "Aylık ve yıllık bütçeyi doğrudan panoda düzenleyin.",
"picker_desc_groups": "Görev gruplarını doğrudan panoda oluşturun, yeniden adlandırın ve silin.",
"picker_desc_vacation": "Tatil modunu doğrudan panoda açıp kapatın ve tarihlerini ayarlayın.",
"picker_desc_battery_fleet": "İzlenen tüm piller: şu anda hangileri zayıf, hangileri yakında bitecek ve ne satın alınmalı.",
"picker_desc_dashboard": "Otomatik oluşturulan pano. Görünümleri alana, duruma, kata veya vadeye göre gruplayın — strateji düzenleyicisinden veya YAML ile seçilir.",
"picker_desc_section": "Bakım görevlerini (alana, duruma, vadeye göre filtrelenebilir) herhangi bir pano görünümüne bölüm olarak yerleştirin.",
"picker_desc_vacation_status": "Kompakt tatil modu durum bandı. Paneldeki ayarları açmak için dokunun.",
"picker_desc_budget_status": "Aylık ve yıllık bakım bütçesine genel bakış. Ayrıntılar için dokunun.",
"picker_desc_groups_status": "Yapılandırılmış görev gruplarının, her birindeki görev sayısıyla listesi.",
"hist_note_button": "Pano düğmesiyle tamamlandı",
"hist_note_todo": "Yapılacaklar listesinden tamamlandı",
"hist_note_voice": "Sesle tamamlandı",
"hist_note_notification": "Bildirimden tamamlandı",
"hist_note_nfc": "NFC etiketiyle tamamlandı",
"hist_note_shopping_list": "Alışveriş listesinden tamamlandı",
"hist_note_todo_mirror": "Yansıtılan bir yapılacaklar listesinden tamamlandı",
"hist_note_skipped_notification": "Bildirimden atlandı",
"hist_note_skipped_button": "Pano düğmesiyle atlandı",
"hist_note_sensor_triggered": "Sensör tetikleyicisi etkinleşti",
"hist_note_initial": "Görev oluşturulurken ayarlanan başlangıç değeri",
"hist_note_auto_recovered": "Otomatik tamamlandı: sensör düzeldi ({value})",
"hist_note_reset": "{date} tarihine sıfırlandı",
"hist_note_trigger_replaced": "Tetikleyici varlık değiştirildi: {old} → {new}",
"hist_note_trigger_removed": "Sensör tetikleyicisi kaldırıldı (varlık: {entity}). Zamanlama {schedule} olarak değiştirildi.",
"hist_note_compound_removed": "{entity} varlığı bileşik tetikleyiciden kaldırıldı; kalan koşul sayısı: {count}.",
"recent_activities": "Son Etkinlikler",
"search_notes": "Notlarda ara",
"avg_cost": "Ort. Maliyet",
@@ -709,6 +788,7 @@
"on_complete_action_desc": "Görev tamamlandığında bir HA servisi çağırır — örn. cihazdaki bir sayacı sıfırlar.",
"on_complete_action_target": "Hedef varlık",
"on_complete_action_target_hint": "Not: varlık alan adı servisle eşleşmelidir — örn. 'button.press' yalnızca button.* üzerinde, 'counter.increment' yalnızca counter.* üzerinde, 'input_button.press' yalnızca input_button.* üzerinde çalışır. Eşleşmezse eylem sessizce başarısız olur (HA günlüğü: 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Görev kendiliğinden tamamlandığında değil (sensörü düzeldi)",
"on_complete_action_data": "Veri (JSON, isteğe bağlı)",
"on_complete_action_test": "Yapılandırmayı doğrula",
"on_complete_action_test_success": "✓ Yapılandırma geçerli (eylem yalnızca görev tamamlandığında çalışır)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "\"{name}\": görevler {before} → {after}",
"adopt_problem_adopt": "Seçilenleri devral",
"adopt_problem_done": "{tasks} sorun sensörü devralındı",
"adopt_problem_done_one": "1 sorun sensörü devralındı",
"views_label": "Görünümler",
"views_none": "— Görünüm yok —",
"views_manage": "Görünümleri kaydet / yönet",
@@ -1013,6 +1094,7 @@
"setups_none": "Bağlanmamış sarf malzemesi sensörü olan desteklenen cihaz bulunamadı.",
"setups_adopt": "Seçilenleri kur",
"setups_done": "{tasks} sensöre bağlı görev oluşturuldu.",
"setups_done_one": "1 sensöre bağlı görev oluşturuldu.",
"complete_parts_used": "Bu sefer kullanılan parçalar",
"part_delete_confirm": "'{name}' parçası silinsin mi? Stok takibi, görev bağlantıları ve açık satın alma hatırlatmaları kaldırılacak.",
"baseline_start_value": "Başlangıç okuması (isteğe bağlı)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "başka bir cihazda aynı model",
"setups_maybe_covered": "Muhtemelen \"{name}\" olarak zaten var",
"setups_already": "Zaten var: {names}",
"setups_reset_line": "Tamamlamak entegrasyondaki sayacı da sıfırlar (“{button}”)",
"setups_reset_enable": "— bunun için düğme etkinleştirilir",
"reset_offers_title": "Tamamlamada sayaçları sıfırla",
"reset_offers_hint": "Bu görevler, entegrasyonun kendi tuttuğu bir sayacı izler. Bağlandığında, görevi burada tamamlamak entegrasyonun sıfırlama düğmesine de basar — aksi halde sayaç işlemeye devam eder ve görevin vadesi hemen yeniden gelir.",
"reset_offers_before": "Şimdi: tamamlamak {integration} içindeki sayacı çalışır halde bırakır",
"reset_offers_after": "Sonra: “{button}” düğmesine de basar",
"reset_offers_renamed": "Kurulumdan sonra yeniden adlandırıldı — bu görevin bu parçaya ait olduğunu kontrol edin",
"reset_offers_apply": "Sıfırlamaları bağla",
"reset_offers_done": "{count} görev artık tamamlandığında sayacını sıfırlıyor",
"reset_offers_done_one": "1 görev artık tamamlandığında sayacını sıfırlıyor",
"setups_before_after": "Nesnenin görevleri: {before} → {after}",
"setups_new_object_count": "Yeni nesne \"{name}\" · görevler: {count}",
"schedule_preview_title": "Sonraki tarihler",
@@ -1046,6 +1138,7 @@
"history_auto": "Otomatik",
"battery_fleet_title": "Pil filosu",
"battery_fleet_none_low": "Tüm piller iyi durumda — değiştirilecek bir şey yok.",
"battery_fleet_progress": "Değiştirilecek: {n}",
"battery_fleet_buy_now": "Şimdi satın al",
"battery_fleet_soon": "Yakında gerekli",
"battery_fleet_soon_hint": "Son değişim tarihinden tahmin edildi — önceden sipariş verin.",
@@ -1099,8 +1192,12 @@
"dismiss": "Kapat",
"gs_label": "Başlarken — kurulumunuz büyüdükçe bu ipuçları kaybolur",
"gs_setups_chip": "Önerilen kurulumlar {n} cihaz buldu (hazır tetikleyicilerle)",
"gs_setups_chip_one": "Önerilen kurulumlar 1 cihaz buldu (hazır tetikleyicilerle)",
"gs_adopt_chip": "{n} sorun sensörü bakım görevine dönüşebilir",
"gs_adopt_chip_one": "1 sorun sensörü bakım görevine dönüşebilir",
"gs_fleet_chip": "Tek tıkla pil filosunu kurun",
"gs_reset_chip": "{n} görev entegrasyondaki sayacını sıfırlayabilir",
"gs_reset_chip_one": "1 görev entegrasyondaki sayacını sıfırlayabilir",
"cal_editor_window": "Varsayılan pencere",
"cal_editor_window_week": "Hafta (7 gün)",
"cal_editor_window_fortnight": "İki hafta (14 gün)",
@@ -241,6 +241,26 @@
"sort_task_name": "Назва завдання",
"all_objects": "Всі об'єкти",
"all_parts": "Усі деталі",
"all_areas": "Усі зони",
"areas_filter": "Фільтр зон…",
"areas_filter_none": "Немає зон, що відповідають фільтру.",
"area_cost_year": "Вартість за {year}",
"area_object_count": "Об'єктів: {n}",
"area_object_count_one": "1 об'єкт",
"area_range_12m": "Останні 12 місяців",
"area_range_all": "За весь час",
"area_task_filter": "Фільтр за завданням…",
"area_kpi_completions": "Виконання",
"area_kpi_total_time": "Загальний час",
"area_cost_per_month": "Вартість за місяцями",
"area_cost_per_year": "Вартість за роками",
"area_cost_per_object": "Вартість за об'єктами",
"area_cost_share": "Частка",
"area_history_section": "Історія (усі об'єкти)",
"area_report_print": "Звіт по зоні (PDF)",
"area_report_title": "Звіт по зоні",
"area_report_period": "Період",
"area_no_costs": "За цей період витрати не записані.",
"no_tasks_yet": "Завдань ще немає",
"add_first_task": "Додати перше завдання",
"trigger_configuration": "Налаштування тригера",
@@ -399,11 +419,70 @@
"qr_download": "Завантажити SVG",
"qr_action_view": "Переглянути",
"qr_action_complete": "Позначити обслуговування виконаним",
"qr_action_quick_complete": "Швидке завершення — без діалогу",
"qr_print_quick_none": "Для кодів швидкого завершення завдання має мати стандартні значення швидкого завершення (вікно завдання → Стандартні значення швидкого завершення) — жодне завдання вибраних об'єктів їх ще не має.",
"qr_url_mode": "Тип посилання",
"qr_mode_companion": "Companion App",
"qr_mode_local": "Локальний (mDNS)",
"qr_mode_server": "URL сервера",
"overview": "Огляд",
"strat_this_month": "Цього місяця",
"strat_later": "Пізніше",
"strat_week": "Тиждень",
"strat_fortnight": "Два тижні",
"strat_month": "Місяць",
"strat_year": "Рік",
"strat_other": "Інше",
"strat_status": "Статус",
"strat_empty_title": "Поки немає об'єктів обслуговування",
"strat_empty_content": "Відкрийте панель обслуговування, щоб додати перший об'єкт — насос басейну, фільтр вентиляції, автомобіль, усе, що потребує планового догляду.",
"strat_open_panel": "Відкрити панель обслуговування",
"strat_add_object": "Додати об'єкт",
"strat_not_loaded": "**Maintenance Supporter** не завантажено. Спершу встановіть/увімкніть інтеграцію.",
"strat_group_by": "Групувати вкладки за",
"strat_group_area": "За зонами (типово)",
"strat_group_status": "За статусом (Прострочено / Спрацювало / Незабаром / Норма)",
"strat_group_floor": "За поверхами (поверхи з HA)",
"strat_group_due_date": "За терміном (Прострочено / Сьогодні / Цього тижня / Цього місяця / Пізніше)",
"strat_group_calendar": "Ковзний календар (Тиждень / Два тижні / Місяць / Рік)",
"strat_editor_help": "Вкладка «Огляд» завжди перша. Порожні групи пропускаються.",
"picker_name_calendar": "Maintenance Supporter — Календар",
"picker_name_panel": "Maintenance Supporter — Панель у картці",
"picker_name_budget": "Maintenance Supporter — Бюджет обслуговування",
"picker_name_groups": "Maintenance Supporter — Групи",
"picker_name_vacation": "Maintenance Supporter — Режим відпустки",
"picker_name_battery_fleet": "Парк батарей",
"picker_name_section": "Maintenance Supporter — Розділ",
"picker_name_vacation_status": "Maintenance Supporter — Статус відпустки",
"picker_name_budget_status": "Maintenance Supporter — Статус бюджету",
"picker_desc_card": "Огляд завдань обслуговування зі швидкими діями.",
"picker_desc_calendar": "Ковзний календар завдань обслуговування з вікнами 7/14/30/365 днів, значками джерела та мітками надійності прогнозу.",
"picker_desc_panel": "Уся панель Maintenance Supporter у вигляді картки — для вкладки в режимі «Панель» або підвкладки дашборду без пункту в бічному меню.",
"picker_desc_budget": "Редагуйте щомісячний і щорічний бюджет прямо на дашборді.",
"picker_desc_groups": "Створюйте, перейменовуйте та видаляйте групи завдань прямо на дашборді.",
"picker_desc_vacation": "Вмикайте й вимикайте режим відпустки та задавайте його дати прямо на дашборді.",
"picker_desc_battery_fleet": "Усі відстежувані батареї: які розряджені зараз, які скоро сядуть і що купити.",
"picker_desc_dashboard": "Автоматично згенерований дашборд. Групування вкладок за зоною, статусом, поверхом або терміном — вибір у редакторі стратегії чи в YAML.",
"picker_desc_section": "Вбудовуйте завдання обслуговування (з фільтром за зоною, статусом, терміном) як розділ у будь-яку вкладку дашборду.",
"picker_desc_vacation_status": "Компактний банер стану режиму відпустки. Торкніться, щоб відкрити налаштування на панелі.",
"picker_desc_budget_status": "Огляд щомісячного та щорічного бюджету на обслуговування. Торкніться, щоб побачити деталі.",
"picker_desc_groups_status": "Список налаштованих груп завдань із кількістю завдань у кожній.",
"hist_note_button": "Виконано кнопкою на дашборді",
"hist_note_todo": "Виконано зі списку справ",
"hist_note_voice": "Виконано голосом",
"hist_note_notification": "Виконано зі сповіщення",
"hist_note_nfc": "Виконано через NFC-тег",
"hist_note_shopping_list": "Виконано зі списку покупок",
"hist_note_todo_mirror": "Виконано з віддзеркаленого списку справ",
"hist_note_skipped_notification": "Пропущено зі сповіщення",
"hist_note_skipped_button": "Пропущено кнопкою на дашборді",
"hist_note_sensor_triggered": "Спрацював тригер датчика",
"hist_note_initial": "Початкове значення задано під час створення завдання",
"hist_note_auto_recovered": "Автоматично виконано: датчик відновився ({value})",
"hist_note_reset": "Скинуто на {date}",
"hist_note_trigger_replaced": "Сутність тригера замінено: {old} → {new}",
"hist_note_trigger_removed": "Тригер датчика видалено (сутність: {entity}). Розклад змінено на «{schedule}».",
"hist_note_compound_removed": "Сутність {entity} видалено зі складеного тригера; залишилось умов: {count}.",
"recent_activities": "Остання активність",
"search_notes": "Пошук у примітках",
"avg_cost": "Сер. вартість",
@@ -716,6 +795,7 @@
"groups_manage_tasks": "Керування призначеннями завдань…",
"groups_open_panel": "Відкрити на панелі",
"on_complete_action_target_hint": "Примітка: домен сутності має відповідати сервісу — напр. 'button.press' працює лише з button.*, 'counter.increment' лише з counter.*, 'input_button.press' лише з input_button.* тощо. У разі невідповідності дія мовчки не виконається (HA запише в журнал 'Referenced entities ... missing or not currently available').",
"on_complete_action_skip_auto": "Не коли завдання виконується саме (датчик відновився)",
"show_all_objects": "Показати всі об'єкти",
"show_all_tasks": "Скинути фільтр — показати всі завдання",
"filter_to_overdue": "Фільтрувати список лише за простроченими",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "«{name}»: завдання {before} → {after}",
"adopt_problem_adopt": "Прийняти вибрані",
"adopt_problem_done": "Прийнято сенсорів проблем: {tasks}",
"adopt_problem_done_one": "Прийнято 1 сенсор проблем",
"views_label": "Подання",
"views_none": "— Без подання —",
"views_manage": "Зберегти / керувати поданнями",
@@ -1013,6 +1094,7 @@
"setups_none": "Підтримуваних пристроїв із неприв'язаними датчиками витратних матеріалів не знайдено.",
"setups_adopt": "Налаштувати вибране",
"setups_done": "Створено завдань із прив'язкою до датчиків: {tasks}.",
"setups_done_one": "Створено 1 завдання з прив'язкою до датчиків.",
"complete_parts_used": "Використані цього разу запчастини",
"part_delete_confirm": "Видалити запчастину '{name}'? Буде видалено облік залишків, зв'язки із завданнями та відкрите нагадування про купівлю.",
"baseline_start_value": "Початкове значення (необов'язково)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "та сама модель на іншому пристрої",
"setups_maybe_covered": "Імовірно, вже є як «{name}»",
"setups_already": "Уже є: {names}",
"setups_reset_line": "Виконання також скидає лічильник в інтеграції («{button}»)",
"setups_reset_enable": "— для цього кнопку буде увімкнено",
"reset_offers_title": "Скидати лічильники під час виконання",
"reset_offers_hint": "Ці завдання стежать за лічильником, який інтеграція веде сама. Після прив'язки виконання завдання тут також натискає кнопку скидання інтеграції — інакше лічильник продовжує йти і термін завдання одразу настає знову.",
"reset_offers_before": "Зараз: під час виконання лічильник у {integration} продовжує йти",
"reset_offers_after": "Потім: також натискається «{button}»",
"reset_offers_renamed": "Перейменовано після налаштування — перевірте, що завдання стосується цієї деталі",
"reset_offers_apply": "Прив'язати скидання",
"reset_offers_done": "Завдань зі скиданням лічильника під час виконання: {count}",
"reset_offers_done_one": "1 завдання тепер скидає лічильник під час виконання",
"setups_before_after": "Завдання об'єкта: {before} → {after}",
"setups_new_object_count": "Новий об'єкт «{name}» · завдання: {count}",
"schedule_preview_title": "Найближчі дати",
@@ -1046,6 +1138,7 @@
"history_auto": "Автоматично",
"battery_fleet_title": "Парк батарей",
"battery_fleet_none_low": "Усі батареї в нормі — нічого міняти.",
"battery_fleet_progress": "До заміни: {n}",
"battery_fleet_buy_now": "Купити зараз",
"battery_fleet_soon": "Скоро знадобиться",
"battery_fleet_soon_hint": "Оцінка за датою останньої заміни — замовте заздалегідь.",
@@ -1099,8 +1192,12 @@
"dismiss": "Приховати",
"gs_label": "Перші кроки — ці підказки зникнуть у міру зростання налаштування",
"gs_setups_chip": "Рекомендовані налаштування: знайдено {n} пристроїв із готовими тригерами",
"gs_setups_chip_one": "Рекомендовані налаштування: знайдено 1 пристрій із готовими тригерами",
"gs_adopt_chip": "{n} датчиків проблем можуть стати завданнями обслуговування",
"gs_adopt_chip_one": "1 датчик проблем може стати завданням обслуговування",
"gs_fleet_chip": "Один клік налаштує парк батарей",
"gs_reset_chip": "Скидання лічильника в інтеграції доступне для завдань: {n}",
"gs_reset_chip_one": "Скидання лічильника в інтеграції доступне для 1 завдання",
"cal_editor_window": "Вікно за замовчуванням",
"cal_editor_window_week": "Тиждень (7 днів)",
"cal_editor_window_fortnight": "Два тижні (14 днів)",
@@ -243,6 +243,26 @@
"sort_task_name": "任务名称",
"all_objects": "所有维护项",
"all_parts": "所有配件",
"all_areas": "所有区域",
"areas_filter": "筛选区域…",
"areas_filter_none": "没有符合筛选条件的区域。",
"area_cost_year": "{year} 年费用",
"area_object_count": "{n} 个维护项",
"area_object_count_one": "1 个维护项",
"area_range_12m": "最近 12 个月",
"area_range_all": "全部时间",
"area_task_filter": "按任务筛选…",
"area_kpi_completions": "完成次数",
"area_kpi_total_time": "总耗时",
"area_cost_per_month": "每月费用",
"area_cost_per_year": "每年费用",
"area_cost_per_object": "各维护项费用",
"area_cost_share": "占比",
"area_history_section": "历史(全部维护项)",
"area_report_print": "区域报告(PDF)",
"area_report_title": "区域报告",
"area_report_period": "期间",
"area_no_costs": "此期间没有记录费用。",
"no_tasks_yet": "尚无任务",
"add_first_task": "添加首个任务",
"trigger_configuration": "触发器配置",
@@ -401,11 +421,70 @@
"qr_download": "下载 SVG",
"qr_action_view": "查看维护信息",
"qr_action_complete": "标记维护为已完成",
"qr_action_quick_complete": "快速完成 — 无对话框",
"qr_print_quick_none": "快速完成二维码需要任务设置快速完成默认值(任务对话框 → 快速完成默认值)— 所选对象的任务尚未设置。",
"qr_url_mode": "链接类型",
"qr_mode_companion": "Companion App",
"qr_mode_local": "本地 (mDNS)",
"qr_mode_server": "服务器 URL",
"overview": "概览",
"strat_this_month": "本月",
"strat_later": "以后",
"strat_week": "一周",
"strat_fortnight": "两周",
"strat_month": "一个月",
"strat_year": "一年",
"strat_other": "其他",
"strat_status": "状态",
"strat_empty_title": "暂无维护项",
"strat_empty_content": "打开维护面板添加你的第一个维护项——泳池泵、空调滤网、车辆,任何需要定期保养的东西。",
"strat_open_panel": "打开维护面板",
"strat_add_object": "添加维护项",
"strat_not_loaded": "**Maintenance Supporter** 未加载。请先安装/启用该集成。",
"strat_group_by": "视图分组方式",
"strat_group_area": "按区域(默认)",
"strat_group_status": "按状态(逾期 / 已触发 / 即将到期 / 正常)",
"strat_group_floor": "按楼层(使用 HA 楼层)",
"strat_group_due_date": "按到期时间(逾期 / 今天 / 本周 / 本月 / 以后)",
"strat_group_calendar": "滚动日历(一周 / 两周 / 一个月 / 一年)",
"strat_editor_help": "“概览”视图始终排在第一位。空分组会被跳过。",
"picker_name_calendar": "Maintenance Supporter — 日历",
"picker_name_panel": "Maintenance Supporter — 面板卡片",
"picker_name_budget": "Maintenance Supporter — 维护预算",
"picker_name_groups": "Maintenance Supporter — 分组",
"picker_name_vacation": "Maintenance Supporter — 度假模式",
"picker_name_battery_fleet": "电池群",
"picker_name_section": "Maintenance Supporter — 分区",
"picker_name_vacation_status": "Maintenance Supporter — 度假状态",
"picker_name_budget_status": "Maintenance Supporter — 预算状态",
"picker_desc_card": "维护任务概览,附带快捷操作。",
"picker_desc_calendar": "维护任务滚动日历,提供 7/14/30/365 天时间窗、来源图标和预测置信度标签。",
"picker_desc_panel": "将完整的 Maintenance Supporter 面板作为卡片——用于面板视图或仪表盘子视图,无需侧边栏入口。",
"picker_desc_budget": "直接在仪表盘上编辑月度和年度预算。",
"picker_desc_groups": "直接在仪表盘上创建、重命名和删除任务组。",
"picker_desc_vacation": "直接在仪表盘上开启或关闭度假模式并设置日期。",
"picker_desc_battery_fleet": "所有跟踪的电池:当前电量低的、即将耗尽的,以及需要购买的。",
"picker_desc_dashboard": "自动生成的仪表盘。可按区域、状态、楼层或到期时间对视图分组——在策略编辑器或 YAML 中选择。",
"picker_desc_section": "将维护任务(可按区域、状态、到期时间过滤)作为分区嵌入任意仪表盘视图。",
"picker_desc_vacation_status": "紧凑的度假模式状态横幅。点按可在面板中打开设置。",
"picker_desc_budget_status": "月度和年度维护预算概览。点按查看详情。",
"picker_desc_groups_status": "已配置的任务组列表,显示每组的任务数。",
"hist_note_button": "通过仪表盘按钮完成",
"hist_note_todo": "通过待办清单完成",
"hist_note_voice": "通过语音完成",
"hist_note_notification": "通过通知完成",
"hist_note_nfc": "通过 NFC 标签完成",
"hist_note_shopping_list": "通过购物清单完成",
"hist_note_todo_mirror": "通过镜像的待办清单完成",
"hist_note_skipped_notification": "通过通知跳过",
"hist_note_skipped_button": "通过仪表盘按钮跳过",
"hist_note_sensor_triggered": "传感器触发器已触发",
"hist_note_initial": "创建任务时设置的初始值",
"hist_note_auto_recovered": "自动完成:传感器已恢复({value})",
"hist_note_reset": "重置为 {date}",
"hist_note_trigger_replaced": "触发实体已替换:{old} → {new}",
"hist_note_trigger_removed": "传感器触发器已移除(原实体:{entity})。计划已转换为{schedule}。",
"hist_note_compound_removed": "已从组合触发器中移除实体 {entity};剩余 {count} 个条件。",
"recent_activities": "近期活动",
"search_notes": "搜索备注",
"avg_cost": "平均成本",
@@ -703,6 +782,7 @@
"on_complete_action_desc": "任务完成时调用 HA 服务 — 例如重置设备上的物理计数器。",
"on_complete_action_target": "目标实体",
"on_complete_action_target_hint": "注意:实体的域必须与服务匹配 — 例如 'button.press' 仅适用于 button.* 实体,'counter.increment' 仅适用于 counter.* 实体等。如果不匹配,动作将静默失败。",
"on_complete_action_skip_auto": "任务自行完成时(其传感器已恢复)不触发",
"on_complete_action_data": "数据(JSON, 可选)",
"on_complete_action_test": "验证配置",
"on_complete_action_test_success": "✓ 配置有效(动作将在任务完成时触发)",
@@ -958,6 +1038,7 @@
"adopt_summary_existing": "“{name}”:任务 {before} → {after}",
"adopt_problem_adopt": "采用所选",
"adopt_problem_done": "已采用 {tasks} 个问题传感器",
"adopt_problem_done_one": "已采用 1 个问题传感器",
"views_label": "视图",
"views_none": "— 无视图 —",
"views_manage": "保存/管理视图",
@@ -1013,6 +1094,7 @@
"setups_none": "未找到具有未连接耗材传感器的受支持设备。",
"setups_adopt": "配置所选项",
"setups_done": "已创建 {tasks} 个传感器联动任务。",
"setups_done_one": "已创建 1 个传感器联动任务。",
"complete_parts_used": "本次使用的部件",
"part_delete_confirm": "删除部件“{name}”?其库存跟踪、任务关联及未完成的购买提醒将被移除。",
"baseline_start_value": "起始读数(可选)",
@@ -1035,6 +1117,16 @@
"setups_reason_sibling": "另一台设备上的相同型号",
"setups_maybe_covered": "可能已存在为“{name}”",
"setups_already": "已存在:{names}",
"setups_reset_line": "完成时也会重置集成中的计数器(“{button}”)",
"setups_reset_enable": "— 为此将启用该按钮",
"reset_offers_title": "完成时重置计数器",
"reset_offers_hint": "这些任务监视由集成自行维护的计数器。关联后,在此完成任务时也会按下集成的重置按钮 — 否则计数器会继续累计,任务会立即再次到期。",
"reset_offers_before": "现在:完成后 {integration} 中的计数器继续累计",
"reset_offers_after": "之后:还会按下“{button}”",
"reset_offers_renamed": "设置后已重命名 — 请确认此任务对应该部件",
"reset_offers_apply": "关联重置",
"reset_offers_done": "{count} 个任务现在会在完成时重置计数器",
"reset_offers_done_one": "1 个任务现在会在完成时重置计数器",
"setups_before_after": "对象的任务:{before} → {after}",
"setups_new_object_count": "新对象“{name}” · 任务:{count}",
"schedule_preview_title": "下次日期",
@@ -1046,6 +1138,7 @@
"history_auto": "自动",
"battery_fleet_title": "电池群",
"battery_fleet_none_low": "所有电池正常 — 无需更换。",
"battery_fleet_progress": "待更换:{n}",
"battery_fleet_buy_now": "现在购买",
"battery_fleet_soon": "即将需要",
"battery_fleet_soon_hint": "根据上次更换日期预测 — 提前订购。",
@@ -1099,8 +1192,12 @@
"dismiss": "忽略",
"gs_label": "入门提示——随着配置的完善,这些提示会自动消失",
"gs_setups_chip": "推荐配置发现 {n} 台设备(含预设触发器)",
"gs_setups_chip_one": "推荐配置发现 1 台设备(含预设触发器)",
"gs_adopt_chip": "{n} 个问题传感器可转换为维护任务",
"gs_adopt_chip_one": "1 个问题传感器可转换为维护任务",
"gs_fleet_chip": "一键设置电池车队",
"gs_reset_chip": "{n} 个任务可以重置集成中的计数器",
"gs_reset_chip_one": "1 个任务可以重置集成中的计数器",
"cal_editor_window": "默认时间窗",
"cal_editor_window_week": "一周(7天)",
"cal_editor_window_fortnight": "两周(14天)",
@@ -8,6 +8,7 @@ import { syncLocaleFromHass, sharedStyles, STATUS_COLORS, t, ensureLocale, isLoc
import { openSignedDocument } from "./helpers/document-url";
import { isSafeHttpUrl } from "./helpers/url";
import { registerCustomCard } from "./helpers/register-card";
import { localizePickerWhenReady } from "./helpers/picker-i18n";
import type {
HomeAssistant,
MaintenanceObjectResponse,
@@ -791,3 +792,5 @@ registerCustomCard({
description: "Overview of your maintenance tasks with quick actions.",
preview: true,
});
// The picker entries of every bundle, in the user's language once HA is up.
localizePickerWhenReady();
@@ -31,6 +31,7 @@
*/
import { ACTIONABLE_STATUSES, STATUS_ICONS } from "./status-constants";
import { ensureLocale, t } from "./helpers/locale-core";
import { loadHistoryEntryDraft } from "./helpers/history-draft";
interface MaintenanceObjectResp {
@@ -70,6 +71,54 @@ interface HassLike {
floors?: Record<string, FloorEntry>;
}
// ── i18n ──────────────────────────────────────────────────────────────────
// The strategy bundle stays small and Lit-free, so it carries no English
// table: every string it shows names its locale key and its English text
// (kept equal to en.json by tests/test_i18n.py). ``_lang`` is set from hass
// before a dashboard is generated or the editor renders.
const STRINGS = {
maintenance: "Maintenance",
overview: "Overview",
overdue: "Overdue",
triggered: "Triggered",
due_soon: "Due Soon",
ok: "OK",
today: "Today",
today_this_week: "This week",
unassigned: "Unassigned",
tasks: "Tasks",
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 — 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.",
} as const;
type StringKey = keyof typeof STRINGS;
let _lang = "en";
const s = (key: StringKey): string => t(key, _lang, STRINGS[key]);
/** Load the user's language before generating (the English fallbacks keep
* working when the fetch fails). */
async function useLanguage(hass: { language?: string } | undefined): Promise<void> {
_lang = hass?.language || "en";
await ensureLocale(_lang);
}
type GroupBy = "area" | "status" | "floor" | "due_date" | "calendar";
interface MaintenanceDashboardStrategyConfig {
@@ -127,21 +176,21 @@ const STATE_NOT_RUNNING = "NOT_RUNNING";
const STATUS_VIEWS: Array<{
status: string;
title: string;
title: StringKey;
icon: string;
path: string;
}> = [
// Icons come from the shared STATUS_ICONS so the generated dashboard
// matches the panel (this table used to hardcode its own set — "Overdue"
// wore the panel's due-soon icon).
{ status: "overdue", title: "Overdue", icon: STATUS_ICONS.overdue, path: "overdue" },
{ status: "triggered", title: "Triggered", icon: STATUS_ICONS.triggered, path: "triggered" },
{ status: "due_soon", title: "Due Soon", icon: STATUS_ICONS.due_soon, path: "due-soon" },
{ status: "ok", title: "OK", icon: STATUS_ICONS.ok, path: "ok" },
{ status: "overdue", title: "overdue", icon: STATUS_ICONS.overdue, path: "overdue" },
{ status: "triggered", title: "triggered", icon: STATUS_ICONS.triggered, path: "triggered" },
{ status: "due_soon", title: "due_soon", icon: STATUS_ICONS.due_soon, path: "due-soon" },
{ status: "ok", title: "ok", icon: STATUS_ICONS.ok, path: "ok" },
];
const DUE_DATE_VIEWS: Array<{
title: string;
title: StringKey;
icon: string;
path: string;
filter: { filter_due_min_days?: number; filter_due_max_days?: number };
@@ -149,7 +198,7 @@ const DUE_DATE_VIEWS: Array<{
matches: (days: number) => boolean;
}> = [
{
title: "Overdue",
title: "overdue",
// The shared overdue icon — "mdi:alert-circle" is the DUE-SOON one, the
// exact drift status-constants.ts was created to end (DRY audit 2026-09-26).
icon: STATUS_ICONS.overdue,
@@ -158,28 +207,28 @@ const DUE_DATE_VIEWS: Array<{
matches: (d) => d <= -1,
},
{
title: "Today",
title: "today",
icon: "mdi:calendar-today",
path: "today",
filter: { filter_due_min_days: 0, filter_due_max_days: 0 },
matches: (d) => d === 0,
},
{
title: "This Week",
title: "today_this_week",
icon: "mdi:calendar-week",
path: "this-week",
filter: { filter_due_min_days: 1, filter_due_max_days: 7 },
matches: (d) => d >= 1 && d <= 7,
},
{
title: "This Month",
title: "strat_this_month",
icon: "mdi:calendar-month",
path: "this-month",
filter: { filter_due_min_days: 8, filter_due_max_days: 30 },
matches: (d) => d >= 8 && d <= 30,
},
{
title: "Later",
title: "strat_later",
icon: "mdi:calendar-clock",
path: "later",
filter: { filter_due_min_days: 31 },
@@ -234,10 +283,10 @@ export function kpiMarkdownCard(ids: SummaryEntityIds = {}, counts: SummaryCount
type: "markdown",
text_only: true,
content: [
`🔴 **${kpiValue("overdue", ids, counts)}** overdue`,
`⚡ **${kpiValue("triggered", ids, counts)}** triggered`,
`🟡 **${kpiValue("due_soon", ids, counts)}** due soon`,
`🟢 **${kpiValue("ok", ids, counts)}** ok`,
`🔴 **${kpiValue("overdue", ids, counts)}** ${s("overdue")}`,
`⚡ **${kpiValue("triggered", ids, counts)}** ${s("triggered")}`,
`🟡 **${kpiValue("due_soon", ids, counts)}** ${s("due_soon")}`,
`🟢 **${kpiValue("ok", ids, counts)}** ${s("ok")}`,
].join(" · "),
};
}
@@ -254,7 +303,7 @@ export function kpiMarkdownCard(ids: SummaryEntityIds = {}, counts: SummaryCount
// rip our object-add dialog out of the panel into a custom element.
function emptyStateView(): ViewConfig {
return {
title: "Maintenance",
title: s("maintenance"),
type: "panel",
cards: [
{
@@ -262,13 +311,12 @@ function emptyStateView(): ViewConfig {
icon: "mdi:wrench-clock",
icon_color: "primary",
content_only: true,
title: "No maintenance objects yet",
content:
"Open the Maintenance panel to add your first object — pool pump, HVAC filter, vehicle, anything that needs scheduled care.",
title: s("strat_empty_title"),
content: s("strat_empty_content"),
buttons: [
{
icon: "mdi:wrench",
text: "Open Maintenance panel",
text: s("strat_open_panel"),
appearance: "filled",
variant: "brand",
tap_action: {
@@ -278,7 +326,7 @@ function emptyStateView(): ViewConfig {
},
{
icon: "mdi:plus",
text: "Add object",
text: s("strat_add_object"),
appearance: "outlined",
variant: "brand",
tap_action: {
@@ -295,7 +343,7 @@ function emptyStateView(): ViewConfig {
function overviewView(summaryIds: SummaryEntityIds = {}, counts: SummaryCounts = {}): ViewConfig {
const kpiCard = kpiMarkdownCard(summaryIds, counts);
return {
title: "Overview",
title: s("overview"),
icon: "mdi:wrench-clock",
path: "overview",
type: "sections",
@@ -313,7 +361,7 @@ function overviewView(summaryIds: SummaryEntityIds = {}, counts: SummaryCounts =
cards: [
{
type: "heading",
heading: "Status",
heading: s("strat_status"),
heading_style: "title",
},
kpiCard,
@@ -321,8 +369,8 @@ function overviewView(summaryIds: SummaryEntityIds = {}, counts: SummaryCounts =
},
],
visibility: [LARGE_SCREEN_CONDITION],
content_label: "Tasks",
sidebar_label: "Status",
content_label: s("tasks"),
sidebar_label: s("strat_status"),
},
sections: [
makeCardSection({
@@ -377,7 +425,7 @@ function viewsByArea(
const unassigned = byArea.get(null);
if (unassigned && unassigned.length > 0) {
views.push({
title: "Unassigned",
title: s("unassigned"),
icon: "mdi:help-circle-outline",
path: "unassigned",
type: "sections",
@@ -406,7 +454,7 @@ function viewsByStatus(objects: MaintenanceObjectResp[]): ViewConfig[] {
for (const v of STATUS_VIEWS) {
if (!present.has(v.status)) continue;
views.push({
title: v.title,
title: s(v.title),
icon: v.icon,
path: v.path,
type: "sections",
@@ -473,7 +521,7 @@ function viewsByFloor(
const unassigned = byFloor.get(null);
if (unassigned && unassigned.length > 0) {
views.push({
title: "Other",
title: s("strat_other"),
icon: "mdi:help-circle-outline",
path: "other",
type: "sections",
@@ -496,19 +544,19 @@ function viewsByCalendar(_objects: MaintenanceObjectResp[]): ViewConfig[] {
// window_days. Window-chips inside the card are hidden because the
// tab-bar already serves as the window selector.
const WINDOWS: Array<{
title: string;
title: StringKey;
icon: string;
path: string;
window_days: 7 | 14 | 30 | 365;
}> = [
{ title: "Week", icon: "mdi:calendar-week", path: "cal-7", window_days: 7 },
{ title: "Fortnight", icon: "mdi:calendar-week-begin", path: "cal-14", window_days: 14 },
{ title: "Month", icon: "mdi:calendar-month", path: "cal-30", window_days: 30 },
{ title: "Year", icon: "mdi:calendar-clock", path: "cal-365", window_days: 365 },
{ title: "strat_week", icon: "mdi:calendar-week", path: "cal-7", window_days: 7 },
{ title: "strat_fortnight", icon: "mdi:calendar-week-begin", path: "cal-14", window_days: 14 },
{ title: "strat_month", icon: "mdi:calendar-month", path: "cal-30", window_days: 30 },
{ title: "strat_year", icon: "mdi:calendar-clock", path: "cal-365", window_days: 365 },
];
return WINDOWS.map((w) => ({
title: w.title,
title: s(w.title),
icon: w.icon,
path: w.path,
type: "panel",
@@ -540,7 +588,7 @@ function viewsByDueDate(objects: MaintenanceObjectResp[]): ViewConfig[] {
DUE_DATE_VIEWS.forEach((v, i) => {
if (!presentBuckets.has(i)) return;
views.push({
title: v.title,
title: s(v.title),
icon: v.icon,
path: v.path,
type: "sections",
@@ -596,6 +644,7 @@ export class MaintenanceDashboardStrategy extends HTMLElement {
};
}
await useLanguage(hass);
let response: { objects: MaintenanceObjectResp[] };
try {
response = await hass.connection.sendMessagePromise<{
@@ -603,17 +652,11 @@ export class MaintenanceDashboardStrategy extends HTMLElement {
}>({ type: "maintenance_supporter/objects" });
} catch {
return {
title: "Maintenance",
title: s("maintenance"),
views: [
{
title: "Maintenance",
cards: [
{
type: "markdown",
content:
"**Maintenance Supporter** is not loaded. Install/enable the integration first.",
},
],
title: s("maintenance"),
cards: [{ type: "markdown", content: s("strat_not_loaded") }],
},
],
};
@@ -626,7 +669,7 @@ export class MaintenanceDashboardStrategy extends HTMLElement {
// "no tasks") — that's a useless first impression. Show an actionable
// empty-state instead so the user knows where to click next.
if (objects.length === 0) {
return { title: "Maintenance", views: [emptyStateView()] };
return { title: s("maintenance"), views: [emptyStateView()] };
}
const groupBy: GroupBy = config?.group_by ?? "area";
@@ -665,7 +708,7 @@ export class MaintenanceDashboardStrategy extends HTMLElement {
} catch { /* fall back to the documented default ids */ }
return {
title: "Maintenance",
title: s("maintenance"),
views: [
overviewView(summaryIds, counts),
...(viewBuilders[groupBy] ?? viewBuilders.area)(),
@@ -684,14 +727,18 @@ export class MaintenanceDashboardStrategy extends HTMLElement {
// We keep the editor as a plain HTMLElement (no Lit dependency) because the
// strategy file otherwise stays Lit-free. It's enough HTML for one <select>.
const GROUP_BY_OPTIONS: Array<{ value: GroupBy; label: string }> = [
{ value: "area", label: "By area (default)" },
{ value: "status", label: "By status (Overdue / Triggered / Due Soon / OK)" },
{ value: "floor", label: "By floor (uses HA floors)" },
{ value: "due_date", label: "By due date (Overdue / Today / Week / Month / Later)" },
{ value: "calendar", label: "Rolling calendar (Week / Fortnight / Month / Year)" },
const GROUP_BY_OPTIONS: Array<{ value: GroupBy; label: StringKey }> = [
{ value: "area", label: "strat_group_area" },
{ value: "status", label: "strat_group_status" },
{ value: "floor", label: "strat_group_floor" },
{ value: "due_date", label: "strat_group_due_date" },
{ value: "calendar", label: "strat_group_calendar" },
];
function escapeHtml(text: string): string {
return text.replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
}
class MaintenanceStrategyEditor extends HTMLElement {
private _config: MaintenanceDashboardStrategyConfig = {
type: "custom:maintenance-supporter",
@@ -699,7 +746,9 @@ class MaintenanceStrategyEditor extends HTMLElement {
private _hass: HassLike | undefined;
set hass(hass: HassLike | undefined) {
const changed = (hass?.language || "en") !== _lang;
this._hass = hass;
if (changed) void useLanguage(hass).then(() => this._render());
}
setConfig(config: MaintenanceDashboardStrategyConfig): void {
@@ -715,7 +764,7 @@ class MaintenanceStrategyEditor extends HTMLElement {
const current = this._config.group_by ?? "area";
const options = GROUP_BY_OPTIONS.map(
(o) =>
`<option value="${o.value}"${o.value === current ? " selected" : ""}>${o.label}</option>`,
`<option value="${o.value}"${o.value === current ? " selected" : ""}>${escapeHtml(s(o.label))}</option>`,
).join("");
this.innerHTML = `
<style>
@@ -734,11 +783,9 @@ class MaintenanceStrategyEditor extends HTMLElement {
}
</style>
<div class="editor">
<label for="group-by">Group views by</label>
<label for="group-by">${escapeHtml(s("strat_group_by"))}</label>
<select id="group-by">${options}</select>
<div class="help">
The "Overview" view is always first. Empty groups are skipped.
</div>
<div class="help">${escapeHtml(s("strat_editor_help"))}</div>
</div>
`;
const select = this.querySelector("#group-by") as HTMLSelectElement | null;
@@ -26,6 +26,7 @@ import { effectivePhase } from "./helpers/phases";
import { renderEventTitles } from "./helpers/event-titles";
import { recommendationReason, type HomeProfile, type TemplateRecommendation } from "./helpers/home-profile";
import { buildCompleteDialogArgs, fillAndOpenCompleteDialog } from "./helpers/complete-dialog-args";
import { areaDisplayName, areaKeyOf } from "./helpers/area-history";
import { panelStyles } from "./panel-styles";
// Global search (#171): what the panel needs to know about a hit — enough
@@ -130,7 +131,8 @@ import { readingSlotDelta } from "./helpers/reading-slots";
import { bulkResultMessage, runWs, runWsEach } from "./helpers/ws-run";
import { STATUS_ORDER, statusRank } from "./status-constants";
type View = "overview" | "object" | "task" | "all_objects" | "all_parts";
// #191: all_areas = the areas page, area = one area's history and costs.
type View = "overview" | "object" | "task" | "all_objects" | "all_parts" | "all_areas" | "area";
/** One row of the instance-wide parts overview (#130) — the WS response of
* `parts/overview`: the stored part fields plus owner, live stock and the
@@ -221,6 +223,8 @@ export class MaintenanceSupporterPanel extends LitElement {
@state() private _allParts: PartsOverviewRow[] | null = null;
@state() private _selectedEntryId: string | null = null;
@state() private _selectedTaskId: string | null = null;
/** #191: the area on the `area` view (HA area id or area-history NO_AREA). */
@state() private _selectedAreaId: string | null = null;
@state() private _filterStatus = "";
@state() private _filterUser: string | null = null;
@state() private _filterLabel: string | null = null;
@@ -272,6 +276,9 @@ export class MaintenanceSupporterPanel extends LitElement {
@state() private _moreMenuOpen = false;
@state() private _objMenuOpen = false;
@state() private _toastMessage = "";
/** error = red (a refusal, a failure); info = neutral like HA's own
* toasts — a success used to show in the error red too (2.95). */
@state() private _toastKind: "error" | "info" = "error";
@state() private _toastUndo: (() => void) | null = null;
@state() private _toastActionLabel = "";
// Narrow-viewport disclosure (UX 2026-07): filters and create-actions are
@@ -284,6 +291,10 @@ export class MaintenanceSupporterPanel extends LitElement {
@state() private _gsSetupsCount = 0;
@state() private _gsAdoptCount = 0;
private _gsLoaded = false;
// 2.95: tasks whose completion could also reset the integration's counter
// — offered to every install (the fix for tasks adopted before), once.
private _resetOffersLoaded = false;
@state() private _resetOffersCount = 0;
// Battery Fleet: offer one-click setup only when Battery Notes is present
// and the fleet isn't set up yet.
@state() private _batteryFleetSetupAvailable = false;
@@ -837,7 +848,7 @@ export class MaintenanceSupporterPanel extends LitElement {
// state-less entry would restore nothing and leave the task page up.
const state = history.state as { msp_view?: View } | null;
if (!state?.msp_view) {
history.replaceState({ msp_view: this._view, msp_entry: this._selectedEntryId, msp_task: this._selectedTaskId }, "");
history.replaceState({ msp_view: this._view, msp_entry: this._selectedEntryId, msp_task: this._selectedTaskId, msp_area: this._selectedAreaId }, "");
}
}
@@ -964,6 +975,17 @@ export class MaintenanceSupporterPanel extends LitElement {
}
}
// #191: `?area=<area_id>` opens that area's page (a dashboard button per
// room); an area no object sits in lands on the areas list instead.
const areaParam = params.get("area");
if (areaParam && !params.get("entry_id")) {
this._deepLinkHandled = true;
cleanMsActionUrl();
if (this._objects.some((o) => areaKeyOf(o.object) === areaParam)) this._showArea(areaParam);
else this._showAllAreas();
return;
}
const entryId = params.get("entry_id");
if (!entryId) return;
this._deepLinkHandled = true;
@@ -1212,6 +1234,7 @@ export class MaintenanceSupporterPanel extends LitElement {
trigger_current_delta: task.trigger_current_delta ?? null,
trigger_config: task.trigger_config ?? null,
trigger_entity_info: task.trigger_entity_info ?? null,
battery_fleet_task: task.battery_fleet_task === true,
times_performed: task.times_performed,
total_cost: task.total_cost,
interval_days: task.interval_days ?? null,
@@ -1293,15 +1316,15 @@ export class MaintenanceSupporterPanel extends LitElement {
/** Push a browser history entry so the back button navigates within the
* panel. An in-app deep link arrives on the entry HA's navigate() just
* pushed — that one is taken over instead (see _onLocationChanged). */
private _pushPanelState(view: View, entryId?: string | null, taskId?: string | null): void {
const state = { msp_view: view, msp_entry: entryId || null, msp_task: taskId || null };
private _pushPanelState(view: View, entryId?: string | null, taskId?: string | null, areaId?: string | null): void {
const state = { msp_view: view, msp_entry: entryId || null, msp_task: taskId || null, msp_area: areaId || null };
if (this._deepLinkInPlace) history.replaceState(state, "");
else history.pushState(state, "");
}
/** Handle browser back/forward button. */
private _onPopState(e: PopStateEvent): void {
const s = e.state as { msp_view?: View; msp_entry?: string; msp_task?: string } | null;
const s = e.state as { msp_view?: View; msp_entry?: string; msp_task?: string; msp_area?: string } | null;
if (!s?.msp_view) {
// No panel state → we're leaving the panel, let HA handle it
return;
@@ -1310,6 +1333,7 @@ export class MaintenanceSupporterPanel extends LitElement {
this._view = s.msp_view;
this._selectedEntryId = s.msp_entry || null;
this._selectedTaskId = s.msp_task || null;
this._selectedAreaId = s.msp_area || null;
this._moreMenuOpen = false;
this._objectSectionOverride = null;
if (s.msp_view === "all_parts") void this._loadAllParts();
@@ -1362,6 +1386,35 @@ export class MaintenanceSupporterPanel extends LitElement {
}
}
// #191: the areas page (sibling of All objects / All parts) and one area.
private _showAllAreas(): void {
this._pushPanelState("all_areas");
this._view = "all_areas";
this._selectedEntryId = null;
this._selectedTaskId = null;
this._scrollContentToTop();
}
private _showArea(areaId: string): void {
this._pushPanelState("area", null, null, areaId);
this._view = "area";
this._selectedAreaId = areaId;
this._selectedEntryId = null;
this._selectedTaskId = null;
this._scrollContentToTop();
}
/** The two area components are a code-split chunk, fetched the first time
* either view renders (every route in — chip, Back, `?area=` — renders).
* Until then the element is an unknown tag; Lit keeps the properties it
* was given and the upgrade picks them up. */
private _areaUi: Promise<unknown> | null = null;
private _ensureAreaUi(): void {
// A failed chunk fetch (connection blip) is retried on the next render.
this._areaUi ??= Promise.all([import("./components/areas-view"), import("./components/area-view")])
.catch(() => { this._areaUi = null; });
}
/** v2.1.0 (Discussion #49 — @byoung79): tap a KPI value to auto-filter
* the task list. Empty string clears the filter (used by "Tasks" KPI). */
private _filterByStatus(status: string): void {
@@ -1568,8 +1621,16 @@ export class MaintenanceSupporterPanel extends LitElement {
// --- Toast ---
private _showToast(msg: string): void {
/** A count sentence with its own singular for exactly one ("1 devices"
* read in every language, 2.95). Both keys literal, so the locale
* hygiene scan sees them. */
private _countText(key: string, oneKey: string, placeholder: string, n: number, L: string = this._lang): string {
return n === 1 ? t(oneKey, L) : t(key, L).replace(`{${placeholder}}`, String(n));
}
private _showToast(msg: string, kind: "error" | "info" = "error"): void {
if (this._toastTimer) clearTimeout(this._toastTimer);
this._toastKind = kind;
this._toastUndo = null;
this._toastActionLabel = "";
this._toastMessage = msg;
@@ -1587,6 +1648,7 @@ export class MaintenanceSupporterPanel extends LitElement {
private _showUndoToast(msg: string, undo: () => void): void {
if (this._toastTimer) clearTimeout(this._toastTimer);
this._toastKind = "info";
this._toastActionLabel = "";
this._toastMessage = msg;
this._toastUndo = undo;
@@ -1886,7 +1948,7 @@ export class MaintenanceSupporterPanel extends LitElement {
const tasks = e.detail?.tasks_created ?? 0;
const created = (e.detail?.created ?? []) as Array<{ entry_id: string; task_id: string; name: string }>;
await this._loadData();
const msg = t("adopt_problem_done", this._lang).replace("{tasks}", String(tasks));
const msg = this._countText("adopt_problem_done", "adopt_problem_done_one", "tasks", tasks);
if (created.length > 0) {
// Adopted tasks are fully configurable from day one (responsible user,
// priority, documents) — surface that with a direct path to the first.
@@ -1900,7 +1962,7 @@ export class MaintenanceSupporterPanel extends LitElement {
}
});
} else {
this._showToast(msg);
this._showToast(msg, "info");
}
}
@@ -1925,7 +1987,7 @@ export class MaintenanceSupporterPanel extends LitElement {
if (obj && tk) {
this._showTask(obj.entry_id, tk.id);
}
this._showToast(t("battery_fleet_setup_done", this._lang));
this._showToast(t("battery_fleet_setup_done", this._lang), "info");
}
private _openSuggestedSetups(): void {
@@ -1933,9 +1995,18 @@ export class MaintenanceSupporterPanel extends LitElement {
.then((d) => d?.open());
}
private _onResetsWired(e: CustomEvent): void {
const count = e.detail?.wired ?? 0;
this._showToast(this._countText("reset_offers_done", "reset_offers_done_one", "count", count), "info");
this._resetOffersLoaded = false;
this._resetOffersCount = 0;
this._maybeLoadResetOffers();
this._loadData();
}
private _onSetupsAdopted(e: CustomEvent): void {
const tasks = e.detail?.tasks_created ?? 0;
this._showToast(t("setups_done", this._lang).replace("{tasks}", String(tasks)));
this._showToast(this._countText("setups_done", "setups_done_one", "tasks", tasks), "info");
this._loadData();
}
@@ -1971,7 +2042,7 @@ export class MaintenanceSupporterPanel extends LitElement {
await this._loadData();
},
successToast: t("template_created", this._lang),
onSuccess: (m) => this._showToast(m),
onSuccess: (m) => this._showToast(m, "info"),
onError: (m) => this._showToast(m),
},
);
@@ -2109,7 +2180,7 @@ export class MaintenanceSupporterPanel extends LitElement {
await this._loadData();
const msg = bulkResultMessage(doneMsg(done.length), failed, this._lang);
if (undo && done.length > 0) this._showUndoToast(msg, () => undo(done));
else this._showToast(msg);
else this._showToast(msg, failed.length > 0 ? "error" : "info");
}
/** One WS message per selected task row. */
@@ -2284,7 +2355,7 @@ export class MaintenanceSupporterPanel extends LitElement {
busy: (b) => { this._actionLoading = b; },
reload: () => this._loadData(),
successToast: opts?.successToast,
onSuccess: (m) => this._showToast(m),
onSuccess: (m) => this._showToast(m, "info"),
onError: (m) => this._showToast(m),
});
return res === undefined ? null : ((res ?? {}) as T);
@@ -2546,9 +2617,10 @@ export class MaintenanceSupporterPanel extends LitElement {
this._showToast(
`${t("reanalyze_result", this._lang)}: ${res.recommended_interval} ${t("days", this._lang)} ` +
`(${t(`confidence_${res.confidence}`, this._lang)}, ${res.data_points} ${t("data_points", this._lang)})`,
"info",
);
} else {
this._showToast(t("reanalyze_insufficient_data", this._lang));
this._showToast(t("reanalyze_insufficient_data", this._lang), "info");
}
}
@@ -2653,7 +2725,7 @@ export class MaintenanceSupporterPanel extends LitElement {
}
return;
}
this._showToast(t("quick_complete_success", this._lang));
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 */ }
@@ -2906,6 +2978,10 @@ export class MaintenanceSupporterPanel extends LitElement {
? this._renderAllObjects()
: this._view === "all_parts"
? this._renderAllParts()
: this._view === "all_areas"
? this._renderAllAreas()
: this._view === "area"
? this._renderArea()
: this._view === "object"
? this._renderObjectDetail()
: this._renderTaskDetail()}
@@ -2955,12 +3031,13 @@ export class MaintenanceSupporterPanel extends LitElement {
<maintenance-suggested-setups-dialog
.hass=${this.hass}
@integration-setups-adopted=${(e: CustomEvent) => this._onSetupsAdopted(e)}
@reset-counters-wired=${(e: CustomEvent) => this._onResetsWired(e)}
></maintenance-suggested-setups-dialog>
<maintenance-saved-views-dialog
.hass=${this.hass}
@saved-views-changed=${(e: CustomEvent<{ views: SavedView[] }>) => this._onSavedViewsChanged(e)}
></maintenance-saved-views-dialog>
${this._toastMessage ? html`<div class="toast">
${this._toastMessage ? html`<div class="toast ${this._toastKind}" role=${this._toastKind === "error" ? "alert" : "status"}>
<span>${this._toastMessage}</span>
${this._toastUndo ? html`<button class="toast-undo" @click=${() => this._runToastUndo()}>${this._toastActionLabel || t("undo", this._lang)}</button>` : nothing}
</div>` : nothing}
@@ -3000,6 +3077,10 @@ export class MaintenanceSupporterPanel extends LitElement {
const task = this._getTask(this._selectedEntryId, this._selectedTaskId);
crumbs.push({ label: task?.name || "Task" });
}
if (this._view === "area" && this._selectedAreaId) {
crumbs.push({ label: t("all_areas", this._lang), action: () => this._showAllAreas() });
crumbs.push({ label: areaDisplayName(this._selectedAreaId, this.hass?.areas, t("no_area", this._lang)) });
}
return html`
<div class="header">
@@ -3009,6 +3090,7 @@ export class MaintenanceSupporterPanel extends LitElement {
.path=${"M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"}
@click=${() => {
if (this._view === "task") this._showObject(this._selectedEntryId!);
else if (this._view === "area") this._showAllAreas();
else this._showOverview();
}}
></ha-icon-button>`
@@ -3726,6 +3808,9 @@ export class MaintenanceSupporterPanel extends LitElement {
<button class="sibling-view-chip" @click=${() => this._showAllParts()}>
<ha-icon icon="mdi:package-variant-closed"></ha-icon> ${t("all_parts", L)}
</button>
<button class="sibling-view-chip" data-view="all_areas" @click=${() => this._showAllAreas()}>
<ha-icon icon="mdi:floor-plan"></ha-icon> ${t("all_areas", L)}
</button>
</div>
<div class="filter-bar">
<label class="filter-field">
@@ -3837,6 +3922,9 @@ export class MaintenanceSupporterPanel extends LitElement {
<button class="sibling-view-chip" @click=${() => this._showAllObjects()}>
<ha-icon icon="mdi:devices"></ha-icon> ${t("all_objects", L)}
</button>
<button class="sibling-view-chip" data-view="all_areas" @click=${() => this._showAllAreas()}>
<ha-icon icon="mdi:floor-plan"></ha-icon> ${t("all_areas", L)}
</button>
</div>
<div class="filter-bar">
<ha-button appearance="plain" @click=${() => this._exportPartsCsv()}>
@@ -3895,6 +3983,59 @@ export class MaintenanceSupporterPanel extends LitElement {
`;
}
// ── #191: areas ──────────────────────────────────────────────────────────
private _renderAllAreas() {
const L = this._lang;
this._ensureAreaUi();
return html`
<div class="breadcrumb">
<ha-icon-button @click=${() => this._showAllObjects()}>
<ha-icon icon="mdi:arrow-left"></ha-icon>
</ha-icon-button>
<span>${t("all_areas", L)}</span>
<button class="sibling-view-chip" @click=${() => this._showAllObjects()}>
<ha-icon icon="mdi:devices"></ha-icon> ${t("all_objects", L)}
</button>
<button class="sibling-view-chip" @click=${() => this._showAllParts()}>
<ha-icon icon="mdi:package-variant-closed"></ha-icon> ${t("all_parts", L)}
</button>
</div>
<maintenance-areas-view
.hass=${this.hass}
.objects=${this._objects}
.showArchived=${this._showArchived}
.currencySymbol=${this._currencySymbol}
@open-area=${(e: CustomEvent<{ areaKey: string }>) => this._showArea(e.detail.areaKey)}
@archived-toggle=${() => { this._showArchived = !this._showArchived; }}
></maintenance-areas-view>
`;
}
private _renderArea() {
if (!this._selectedAreaId) return nothing;
const L = this._lang;
this._ensureAreaUi();
return html`
<div class="breadcrumb">
<ha-icon-button @click=${() => this._showAllAreas()}>
<ha-icon icon="mdi:arrow-left"></ha-icon>
</ha-icon-button>
<span>${t("all_areas", L)}</span>
</div>
<maintenance-area-view
.hass=${this.hass}
.areaKey=${this._selectedAreaId}
.objects=${this._objects}
.showArchived=${this._showArchived}
.currencySymbol=${this._currencySymbol}
.userName=${(id: string) => this._userService?.getUserName(id) ?? null}
@open-object=${(e: CustomEvent<{ entryId: string }>) => this._showObject(e.detail.entryId)}
@open-task=${(e: CustomEvent<{ entryId: string; taskId: string }>) => this._showTask(e.detail.entryId, e.detail.taskId)}
></maintenance-area-view>
`;
}
/** Client-side CSV of the loaded overview rows (the objects table's CSV is
* server-built; the parts rows are already fully materialized here). */
private _exportPartsCsv(): void {
@@ -4362,6 +4503,11 @@ export class MaintenanceSupporterPanel extends LitElement {
? html`<p class="meta">${[o.manufacturer, o.model].filter(Boolean).join(" ")}</p>`
: nothing}
${o.serial_number ? html`<p class="meta">${t("serial_number_label", L)}: ${o.serial_number}</p>` : nothing}
${o.area_id
? html`<p class="meta">${t("area", L)}:
<a href="#" class="object-area-link" @click=${(e: Event) => { e.preventDefault(); this._showArea(o.area_id!); }}
>${areaDisplayName(o.area_id, this.hass?.areas, t("no_area", L))}</a></p>`
: nothing}
${isSafeHttpUrl(o.documentation_url)
? html`<p class="meta">${t("documentation_url_label", L)}:
<a href=${o.documentation_url} target="_blank" rel="noopener noreferrer">${o.documentation_url}</a>
@@ -4598,7 +4744,17 @@ export class MaintenanceSupporterPanel extends LitElement {
/** Discovery counts for the chips — fetched ONCE per panel life and ONLY
* while the install is young, so mature installs pay nothing. */
private _maybeLoadResetOffers(): void {
if (this._resetOffersLoaded) return;
this._resetOffersLoaded = true;
this.hass.connection
.sendMessagePromise<{ offers: unknown[] }>({ type: "maintenance_supporter/integration_setups/reset_offers" })
.then((r) => { this._resetOffersCount = (r.offers || []).length; })
.catch(() => { this._resetOffersCount = 0; });
}
private _maybeLoadGettingStarted(): void {
this._maybeLoadResetOffers();
if (this._gsLoaded || !this._isYoungInstall()) return;
this._gsLoaded = true;
this.hass.connection
@@ -4618,17 +4774,24 @@ export class MaintenanceSupporterPanel extends LitElement {
if (young && this._gsSetupsCount > 0 && !dismissed.has("setups")) {
chips.push({
id: "setups", icon: "mdi:auto-fix",
text: t("gs_setups_chip", L).replace("{n}", String(this._gsSetupsCount)),
text: this._countText("gs_setups_chip", "gs_setups_chip_one", "n", this._gsSetupsCount, L),
run: () => this._openSuggestedSetups(),
});
}
if (young && this._gsAdoptCount > 0 && !dismissed.has("adopt")) {
chips.push({
id: "adopt", icon: "mdi:alert-circle-check-outline",
text: t("gs_adopt_chip", L).replace("{n}", String(this._gsAdoptCount)),
text: this._countText("gs_adopt_chip", "gs_adopt_chip_one", "n", this._gsAdoptCount, L),
run: () => this._openAdoptProblemSensors(),
});
}
if (this._resetOffersCount > 0 && !dismissed.has("resets")) {
chips.push({
id: "resets", icon: "mdi:counter",
text: this._countText("gs_reset_chip", "gs_reset_chip_one", "n", this._resetOffersCount, L),
run: () => this._openSuggestedSetups(),
});
}
if (this._batteryFleetSetupAvailable && !dismissed.has("fleet")) {
chips.push({
id: "fleet", icon: "mdi:battery-sync",
@@ -951,6 +951,72 @@ export const panelStyles = css`
.objects-table .oc-task_count,
.objects-table .oc-actions { text-align: center; }
/* #191: the areas page + the area detail — light-DOM components, so they
reuse the objects table, filter bar and KPI cards above and add only
what is theirs: sortable headers, count pills, the cost chart. */
.objects-table th.num,
.objects-table td.num { text-align: right; }
.area-sort {
display: inline-flex; align-items: center; gap: 2px;
padding: 0; border: none; background: none; cursor: pointer;
font: inherit; font-weight: 600; color: inherit;
}
.area-sort.active { color: var(--primary-text-color); }
.area-sort ha-icon { --mdc-icon-size: 14px; }
.area-row-icon { --mdc-icon-size: 18px; color: var(--secondary-text-color); margin-right: 8px; vertical-align: middle; }
.area-count {
display: inline-block; min-width: 20px; padding: 0 7px; border-radius: 10px;
text-align: center; font-size: 12px; font-weight: 600;
background: var(--secondary-background-color); color: var(--secondary-text-color);
}
.area-count.overdue { background: color-mix(in srgb, var(--error-color, #f44336) 15%, transparent); color: var(--error-color, #f44336); }
.area-count.due-soon { background: color-mix(in srgb, var(--warning-color, #ff9800) 18%, transparent); color: var(--warning-color, #ff9800); }
.area-filter-bar, .area-filters { justify-content: flex-start; }
.area-filter-bar { align-items: center; }
.area-loading { font-size: 12px; color: var(--secondary-text-color); }
.area-title { display: flex; align-items: center; gap: 8px; }
.area-title ha-icon { --mdc-icon-size: 24px; color: var(--secondary-text-color); }
.area-range-chips { margin: 8px 0 4px; }
.area-range-chips .filter-chip { font-family: inherit; }
.area-filters ms-date-field { min-width: 150px; }
.kpi-bar.area-kpis { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); margin: 12px 0 8px; }
.area-empty { color: var(--secondary-text-color); font-style: italic; font-size: 13px; }
.area-chart { margin: 4px 0 8px; }
.area-chart-max { font-size: 11px; color: var(--secondary-text-color); padding-bottom: 2px; }
/* Hairline gridline at the maximum (labelled above) and the baseline;
bars <= 24px with a 2px gap and a rounded data end (dataviz specs). */
.area-chart-bars {
display: flex; align-items: flex-end; gap: 2px; height: 140px;
border-top: 1px solid var(--divider-color); border-bottom: 1px solid var(--divider-color);
}
.area-bar { flex: 1 1 0; min-width: 0; height: 100%; display: flex; align-items: flex-end; justify-content: center; }
.area-bar-fill { width: 100%; max-width: 24px; min-height: 1px; background: var(--primary-color); border-radius: 4px 4px 0 0; }
.area-bar:hover .area-bar-fill { opacity: 0.75; }
.area-chart-axis { display: flex; gap: 2px; margin-top: 4px; }
.area-bar-label {
flex: 1 1 0; min-width: 0; text-align: center;
font-size: 10.5px; line-height: 1.25; color: var(--secondary-text-color); white-space: nowrap;
}
.area-chart-axis.grouped .area-bar-label { text-align: left; }
.area-share {
display: inline-block; width: 48px; height: 6px; margin-right: 6px; border-radius: 3px;
background: var(--secondary-background-color); vertical-align: middle; overflow: hidden;
}
.area-share-fill { display: block; height: 100%; background: var(--primary-color); }
.area-entry-type { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; font-weight: 500; }
.area-entry-type ha-icon { --mdc-icon-size: 16px; }
.area-object-link, .area-task-link {
padding: 0; border: none; background: none; cursor: pointer;
font: inherit; color: inherit; text-align: left;
}
.area-object-link:hover, .area-task-link:hover { color: var(--primary-color); text-decoration: underline; }
.area-task-link { font-weight: 500; }
.area-more { margin-top: 6px; }
.area-cap-note { margin: 8px 0 0; font-size: 11px; color: var(--secondary-text-color); }
/* A date field carries its keyboard toggle and clear button: on a phone
each gets its own line, or the date itself is cut off. */
:host([narrow]) .area-filters ms-date-field { flex: 1 1 100%; }
.user-badge {
display: inline-flex;
align-items: center;
@@ -2050,6 +2116,8 @@ export const panelStyles = css`
align-items: center;
gap: 16px;
}
/* 2.95: confirmations are neutral like HA's own toasts; red is for errors. */
.toast.info { background: var(--ms-toast-info-bg, #323232); }
.toast-undo {
font: inherit; font-weight: 600; color: #fff; cursor: pointer;
background: transparent; border: 1px solid rgba(255,255,255,.6);
@@ -19,6 +19,7 @@ import { historyPhotoIds } from "../helpers/history-photos";
import { renderRefChip } from "../helpers/reference";
import { entryReadingValues } from "../helpers/reading-slots";
import { matchesQuery } from "../helpers/search-match";
import { historyNoteText } from "../helpers/history-note";
/** v2.2.0: only "lifecycle + cost-bearing" entries are user-editable.
* Triggers are auto-generated by sensors and shouldn't be retroactively
@@ -131,7 +132,13 @@ export function renderHistoryList(task: MaintenanceTask, ctx: HistoryContext) {
if (ctx.search) {
const search = ctx.search.toLowerCase();
// #170: "8.3-2" (or just "-2") in the notes filter finds the entry by number.
filtered = filtered.filter((h) => matchesQuery(h.notes, ctx.search) || (entryRefOf(ctx, h) ?? "").toLowerCase().endsWith(search));
// The shown (translated) text of a note the backend wrote matches too.
filtered = filtered.filter(
(h) =>
matchesQuery(h.notes, ctx.search) ||
matchesQuery(historyNoteText(h.notes, L), ctx.search) ||
(entryRefOf(ctx, h) ?? "").toLowerCase().endsWith(search),
);
}
if (filtered.length === 0) {
@@ -213,7 +220,7 @@ export function renderHistoryEntry(entry: HistoryEntry, ctx: HistoryEntryContext
: nothing}
</div>
<div class="history-date">${formatDateTime(entry.timestamp, L)}</div>
${entry.notes ? html`<div>${entry.notes}</div>` : nothing}
${entry.notes ? html`<div>${historyNoteText(entry.notes, L)}</div>` : nothing}
${renderHistoryPhotos(ctx.hass, entry)}
${renderHistoryReadings(entry, ctx)}
${entry.cost != null || entry.duration != null || entry.trigger_value != null
@@ -185,6 +185,18 @@ export function renderTriggerProgress(row: TaskRow | MaintenanceTask, opts?: { t
return nothing;
}
// The battery-fleet task's sensor counts the batteries to replace against
// an "above 0" limit — the generic label read "4.0 / 0 batteries", like a
// broken ratio, and the zero limit's 100-wide scale painted an empty fleet
// as a full red bar. Say what the number means and fill toward the count
// that triggers.
if (row.battery_fleet_task && triggerType === "threshold" && row.trigger_current_value != null) {
const count = Math.round(row.trigger_current_value);
const limit = tc.trigger_above ?? 0;
pct = count > limit ? 100 : limit > 0 ? Math.max(0, (count / (limit + 1)) * 100) : 0;
label = t("battery_fleet_progress", opts?.lang ?? "en").replace("{n}", formatNumber(count, opts?.lang, 0));
}
const triggerOverflow = pct >= 100;
const barColor = pct > 90 ? "var(--error-color, #f44336)"
: pct > 70 ? "var(--warning-color, #ff9800)"
@@ -2,7 +2,6 @@
import { css } from "lit";
import EN from "./locales/en.json";
import { BUNDLE_VERSION } from "./helpers/bundle-version";
// Display fallback when the backend hasn't sent a currency symbol. The backend
// derives the real symbol from const.BUDGET_CURRENCIES[DEFAULT_BUDGET_CURRENCY].
@@ -24,84 +23,19 @@ export { STATUS_COLORS, STATUS_ICONS } from "./status-constants";
/* ─── i18n ─── */
interface Translations {
[key: string]: string;
}
// English is bundled (imported above) as the always-available fallback so
// the panel renders instantly with a complete table — never an
// untranslated-key flash, even if a locale fetch fails. The other 17
// languages live in served JSON (frontend/locales/<lang>.json) and are
// fetched on demand by ensureLocale(), so a translation edit needs no bundle
// rebuild — the fix for the recurring "stale bundle ships English to non-EN
// users" pitfall. Steady-state t() is the same sync lookup as before.
const DEFAULT_LANG = "en";
// The locale store MUST be shared across bundle copies of this module.
// maintenance-card.js is loaded globally (extra_module_url) and defines the
// dialog custom elements first-wins — so a <maintenance-task-dialog> inside
// the panel runs the CARD bundle's copy of this module. With a module-scoped
// store, the panel's ensureLocale() fills only the PANEL copy and the dialog
// stays English while the rest of the panel is localized (v2.17.0 regression,
// invisible before the runtime-locale split because every bundle inlined all
// languages). One window-scoped store + inflight map keeps every copy reading
// and writing the same tables.
interface LocaleGlobals {
store: Record<string, Translations>;
inflight: Record<string, Promise<void>>;
}
const _localeGlobals: LocaleGlobals = (() => {
const w = window as unknown as { __msLocales?: LocaleGlobals };
if (!w.__msLocales) w.__msLocales = { store: {}, inflight: {} };
return w.__msLocales;
})();
const STORE = _localeGlobals.store;
/** Seed/extend the shared English table with this bundle's copy.
*
* MERGE, not first-wins (#135 regression): after an update, a cached app
* shell can still list an OLD sibling bundle (the card) next to the fresh
* panel. First-wins let the stale bundle's EN — missing every new key —
* claim the store, and the fresh panel rendered raw keys
* ("BATTERY_FLEET_ADD"). Existing entries win conflicts (steady state is
* unchanged); every bundle contributes the keys it knows, so a newer
* bundle's keys can never be shadowed by an older first-loader.
*/
export function seedEnglish(en: Record<string, string>): void {
STORE.en = Object.assign({}, en, STORE.en ?? {});
}
// untranslated-key flash, even if a locale fetch fails. The other languages
// live in served JSON (frontend/locales/<lang>.json) and are fetched on
// demand by ensureLocale(), so a translation edit needs no bundle rebuild.
// The store and lookup live in the Lit-free helpers/locale-core.ts (the
// dashboard strategy uses them too); re-exported here so every existing
// `from "./styles"` import keeps working.
import { ensureLocale, isLocaleLoaded, normLang, seedEnglish, t, type Translations } from "./helpers/locale-core";
import { primeBackendErrors } from "./helpers/backend-errors";
export { ensureLocale, isLocaleLoaded, seedEnglish, setLocale, t } from "./helpers/locale-core";
seedEnglish(EN as Translations);
/** Languages available as runtime-loaded JSON. Keep in sync with locales/. */
const SUPPORTED_LANGS = new Set<string>([
"de", "nl", "fr", "it", "es", "pt", "pt-br", "ru", "uk", "pl", "cs", "sv", "zh",
"da", "fi", "nb", "ja", "hi", "hu", "ko", "tr",
]);
/** Served base for the runtime locale files (mirrors LOCALES_URL in const.py). */
const LOCALES_BASE = "/maintenance_supporter_locales";
const _localeInflight = _localeGlobals.inflight;
/** Normalize an HA language code to our table key.
*
* Brazilian Portuguese is the one regional variant with its OWN table
* ("pt-br") — it must not collapse into European "pt". Mirrors
* normalize_language_code in helpers/i18n.py.
*/
function normLang(lang?: string): string {
const l = (lang || DEFAULT_LANG).toLowerCase();
if (l.startsWith("pt") && l.endsWith("br")) return "pt-br";
return l.substring(0, 2);
}
/** Get a localized string. Falls back to English, then to the key itself. */
export function t(key: string, lang?: string): string {
const l = normLang(lang);
return STORE[l]?.[key] ?? STORE.en[key] ?? key;
}
/** The per-`updated()` locale boot every top-level surface needs: date/time
* prefs follow the HA profile (#97), and the user's UI language is lazily
* fetched with a re-render once it arrives (EN is bundled, so strings read
@@ -121,6 +55,8 @@ export function syncLocaleFromHass(
if (lang && !isLocaleLoaded(lang)) {
ensureLocale(lang).then(() => host.requestUpdate());
}
// Refusals the backend sends with a translation key read in this language.
primeBackendErrors(host.hass as Parameters<typeof primeBackendErrors>[0]);
}
/**
@@ -134,55 +70,6 @@ export function langOf(hass?: { language?: string }): string {
return hass?.language || "en";
}
/** True when *lang*'s table is in memory (English is always bundled). */
export function isLocaleLoaded(lang?: string): boolean {
const l = normLang(lang);
return l === DEFAULT_LANG || l in STORE;
}
/**
* Fetch *lang*'s table once and cache it. Resolves immediately for English,
* an already-loaded language, or an unsupported one (which keeps the English
* fallback). Never rejects: a failed fetch silently leaves English in place,
* matching t()'s fall-through. Callers re-render on resolution.
*/
export function ensureLocale(lang?: string): Promise<void> {
const l = normLang(lang);
if (l === DEFAULT_LANG || l in STORE || !SUPPORTED_LANGS.has(l)) {
return Promise.resolve();
}
if (!(l in _localeInflight)) {
// Version-busted (#135, same family as #124): the locale files are served
// without Cache-Control, so browsers cache them heuristically — after an
// update a stale table would miss every new key and silently fall back to
// English for them. "dev" builds keep a stable URL.
_localeInflight[l] = fetch(`${LOCALES_BASE}/${l}.json?v=${BUNDLE_VERSION}`)
.then((r) => (r.ok ? r.json() : null))
.then((data) => {
if (data) {
STORE[l] = data as Translations;
} else {
// Don't make one failed fetch sticky for the whole page session —
// the next ensureLocale() call retries.
delete _localeInflight[l];
}
})
.catch(() => {
delete _localeInflight[l];
});
}
return _localeInflight[l];
}
/**
* Synchronously seed a locale table, bypassing the fetch. For tests and for
* callers that already hold a table (preload/SSR); the loader and t() share the
* same STORE, so a seeded language reads immediately.
*/
export function setLocale(lang: string, table: Translations): void {
STORE[normLang(lang)] = table;
}
/** Map language table key to BCP-47 locale for date formatting. */
function langToLocale(lang?: string): string {
const l = normLang(lang);
@@ -265,6 +265,8 @@ export interface MaintenanceTask {
service: string; // "domain.service"
target?: { entity_id?: string | string[]; device_id?: string | string[]; area_id?: string | string[] };
data?: Record<string, unknown>;
/** 2.95: not when the task completes itself (a counter reset). */
skip_auto?: boolean;
} | null;
quick_complete_defaults?: {
notes?: string;
@@ -595,6 +597,8 @@ export interface TaskRow {
group_names: string[];
/** #189: titles of the calendar events behind next_due (calendar kind). */
next_event_titles: string[];
/** The battery-fleet task (its progress label counts batteries to replace). */
battery_fleet_task?: boolean;
}
// HomeAssistant type (minimal for our needs)
@@ -12,6 +12,8 @@
* message when nothing matches, so the user never sees a silent failure.
*/
import { t } from "./styles";
import { localizeBackendError } from "./helpers/backend-errors";
import { normLang } from "./helpers/locale-core";
/**
* Every error code the backend's `send_error(msg["id"], "<code>", …)` calls
@@ -214,10 +216,27 @@ export function describeWsError(e: unknown, lang: string, fallback?: string): st
if (typeof e === "string") return e;
if (typeof e !== "object" || e === null) return fallback;
const err = e as { message?: string; error?: { message?: string; code?: string }; code?: string };
type WsErr = {
message?: string;
code?: string;
translation_key?: string;
translation_domain?: string;
translation_placeholders?: Record<string, unknown> | null;
};
const err = e as WsErr & { error?: WsErr };
const raw = err.message || err.error?.message || "";
const code = err.code || err.error?.code || "";
const codeKey = code ? WS_ERROR_CODE_KEYS[code] : undefined;
// A refusal the backend sent with a translation key reads in full, in the
// user's language — Home Assistant ships those texts (strings.json
// `exceptions`, helpers/backend-errors.ts).
const translated = localizeBackendError(
err.translation_domain ?? err.error?.translation_domain,
err.translation_key ?? err.error?.translation_key,
err.translation_placeholders ?? err.error?.translation_placeholders,
);
if (translated) return translated;
if (!raw && !codeKey) return fallback;
const parsed = _parse(raw);
@@ -245,7 +264,11 @@ export function describeWsError(e: unknown, lang: string, fallback?: string): st
default:
if (codeKey) {
const headline = t(codeKey, lang);
return raw && !_restatesCode(raw, code) ? `${headline} (${raw})` : headline;
// The server's own detail is English: it rides along for English
// users only (i18n audit 2026-09-27 — "Limit reached (This object
// already has …)" read half English everywhere else).
const english = normLang(lang) === "en";
return english && raw && !_restatesCode(raw, code) ? `${headline} (${raw})` : headline;
}
// Unknown shape — show the raw message so debugging is possible
return raw || fallback;