126 files

This commit is contained in:
Home Assistant Version Control
2026-08-10 04:41:24 +00:00
parent a7eec430e8
commit df4e74f056
126 changed files with 1522 additions and 9695 deletions
@@ -836,7 +836,6 @@
"update_reload": "Načíst znovu",
"battery_fleet_forecast_overdue": "Předpovězené datum uplynulo — baterie stále hlásí dobrý stav. Pokud jste ji vyměnili, zaznamenejte výměnu; jinak byla předpověď mylná.",
"cost_from_parts": "Použít ≈ {amount} z dílů",
"new_menu": "Nový",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Genindlæs",
"battery_fleet_forecast_overdue": "Forudsagt dato er overskredet — batteriet melder stadig god tilstand. Hvis du har skiftet det, registrér udskiftningen; ellers ramte prognosen forbi.",
"cost_from_parts": "Brug ≈ {amount} fra dele",
"new_menu": "Ny",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Neu laden",
"battery_fleet_forecast_overdue": "Prognosedatum überschritten — die Batterie meldet sich weiterhin gesund. Falls du sie gewechselt hast, trage den Wechsel nach; andernfalls lag die Prognose daneben.",
"cost_from_parts": "≈ {amount} aus Teilen übernehmen",
"new_menu": "Neu",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Reload",
"battery_fleet_forecast_overdue": "Predicted date passed — the battery still reports healthy. If you swapped it, record the replacement; otherwise the forecast was off.",
"cost_from_parts": "Use ≈ {amount} from parts",
"new_menu": "New",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Recargar",
"battery_fleet_forecast_overdue": "Fecha prevista superada: la batería sigue informando buen estado. Si la cambiaste, registra el reemplazo; si no, la previsión falló.",
"cost_from_parts": "Usar ≈ {amount} de las piezas",
"new_menu": "Nuevo",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Lataa uudelleen",
"battery_fleet_forecast_overdue": "Ennustettu päivä on ohitettu — akku ilmoittaa yhä hyvästä kunnosta. Jos vaihdoit sen, kirjaa vaihto; muuten ennuste oli pielessä.",
"cost_from_parts": "Käytä ≈ {amount} osista",
"new_menu": "Uusi",
"dismiss": "Hylkää",
"gs_label": "Aloitus — nämä vihjeet poistuvat asennuksen kasvaessa",
"gs_setups_chip": "Ehdotetut asetukset löysivät {n} laitetta valmiilla laukaisimilla",
@@ -836,7 +836,6 @@
"update_reload": "Recharger",
"battery_fleet_forecast_overdue": "Date prévue dépassée — la batterie se signale toujours en bon état. Si vous l'avez remplacée, enregistrez le remplacement ; sinon la prévision était erronée.",
"cost_from_parts": "Reprendre ≈ {amount} des pièces",
"new_menu": "Nouveau",
"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",
@@ -836,7 +836,6 @@
"update_reload": "पुनः लोड करें",
"battery_fleet_forecast_overdue": "अनुमानित तिथि बीत गई — बैटरी अब भी अच्छी स्थिति बता रही है। यदि आपने इसे बदला है, तो बदलाव दर्ज करें; अन्यथा पूर्वानुमान गलत था।",
"cost_from_parts": "पुर्ज़ों से ≈ {amount} लें",
"new_menu": "नया",
"dismiss": "हटाएँ",
"gs_label": "शुरुआत — सेटअप बढ़ने पर ये संकेत हट जाते हैं",
"gs_setups_chip": "सुझाए गए सेटअप: पूर्व-निर्धारित ट्रिगर वाले {n} उपकरण मिले",
@@ -836,7 +836,6 @@
"update_reload": "Újratöltés",
"battery_fleet_forecast_overdue": "Az előrejelzett dátum elmúlt — az elem továbbra is jó állapotot jelez. Ha kicserélted, rögzítsd a cserét; különben az előrejelzés tévedett.",
"cost_from_parts": "≈ {amount} átvétele az alkatrészekből",
"new_menu": "Új",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Ricarica",
"battery_fleet_forecast_overdue": "Data prevista superata — la batteria risulta ancora in buono stato. Se l'hai sostituita, registra la sostituzione; altrimenti la previsione era errata.",
"cost_from_parts": "Usa ≈ {amount} dai ricambi",
"new_menu": "Nuovo",
"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",
@@ -836,7 +836,6 @@
"update_reload": "再読み込み",
"battery_fleet_forecast_overdue": "予測日を過ぎましたが、電池はまだ正常と報告しています。交換済みなら交換を記録してください。そうでなければ予測が外れています。",
"cost_from_parts": "部品から ≈ {amount} を適用",
"new_menu": "新規",
"dismiss": "閉じる",
"gs_label": "はじめに — セットアップが進むとこれらのヒントは消えます",
"gs_setups_chip": "推奨セットアップ:トリガー設定済みのデバイスを{n}台検出",
@@ -836,7 +836,6 @@
"update_reload": "새로 고침",
"battery_fleet_forecast_overdue": "예측 날짜가 지났지만 배터리는 여전히 정상으로 보고됩니다. 교체했다면 교체를 기록하세요. 아니라면 예측이 빗나간 것입니다.",
"cost_from_parts": "부품에서 ≈ {amount} 적용",
"new_menu": "새로 만들기",
"dismiss": "닫기",
"gs_label": "시작하기 — 설정이 늘어나면 이 힌트는 사라집니다",
"gs_setups_chip": "추천 설정: 트리거가 준비된 기기 {n}대 발견",
@@ -836,7 +836,6 @@
"update_reload": "Last inn på nytt",
"battery_fleet_forecast_overdue": "Forutsagt dato er passert — batteriet melder fortsatt god tilstand. Hvis du byttet det, registrer byttet; ellers bommet prognosen.",
"cost_from_parts": "Bruk ≈ {amount} fra deler",
"new_menu": "Ny",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Herladen",
"battery_fleet_forecast_overdue": "Voorspelde datum verstreken — de batterij meldt zich nog steeds gezond. Heb je hem vervangen, registreer dan de vervanging; anders zat de voorspelling ernaast.",
"cost_from_parts": "≈ {amount} uit onderdelen overnemen",
"new_menu": "Nieuw",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Załaduj ponownie",
"battery_fleet_forecast_overdue": "Przewidywana data minęła — bateria nadal zgłasza dobry stan. Jeśli ją wymieniono, zapisz wymianę; w przeciwnym razie prognoza była błędna.",
"cost_from_parts": "Użyj ≈ {amount} z części",
"new_menu": "Nowy",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Recarregar",
"battery_fleet_forecast_overdue": "Data prevista ultrapassada — a bateria ainda reporta bom estado. Se você a trocou, registre a troca; caso contrário, a previsão errou.",
"cost_from_parts": "Usar ≈ {amount} das peças",
"new_menu": "Novo",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Recarregar",
"battery_fleet_forecast_overdue": "Data prevista ultrapassada — a bateria continua a reportar bom estado. Se a substituiu, registe a substituição; caso contrário, a previsão falhou.",
"cost_from_parts": "Usar ≈ {amount} das peças",
"new_menu": "Novo",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Перезагрузить",
"battery_fleet_forecast_overdue": "Прогнозируемая дата прошла — батарея по-прежнему сообщает о хорошем состоянии. Если вы её заменили, зафиксируйте замену; иначе прогноз оказался неверным.",
"cost_from_parts": "Взять ≈ {amount} из запчастей",
"new_menu": "Создать",
"dismiss": "Скрыть",
"gs_label": "Первые шаги — эти подсказки исчезнут по мере роста настройки",
"gs_setups_chip": "Рекомендуемые настройки: найдено {n} устройств с готовыми триггерами",
@@ -836,7 +836,6 @@
"update_reload": "Ladda om",
"battery_fleet_forecast_overdue": "Förutsagt datum har passerat — batteriet rapporterar fortfarande god status. Om du bytte det, registrera bytet; annars slog prognosen fel.",
"cost_from_parts": "Använd ≈ {amount} från delar",
"new_menu": "Ny",
"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",
@@ -836,7 +836,6 @@
"update_reload": "Yeniden yükle",
"battery_fleet_forecast_overdue": "Öngörülen tarih geçti — pil hâlâ sağlıklı görünüyor. Değiştirdiyseniz değişimi kaydedin; aksi halde tahmin yanılmış demektir.",
"cost_from_parts": "Parçalardan ≈ {amount} kullan",
"new_menu": "Yeni",
"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)",
@@ -836,7 +836,6 @@
"update_reload": "Перезавантажити",
"battery_fleet_forecast_overdue": "Прогнозована дата минула — батарея й далі повідомляє про добрий стан. Якщо ви її замінили, зафіксуйте заміну; інакше прогноз не справдився.",
"cost_from_parts": "Узяти ≈ {amount} із запчастин",
"new_menu": "Створити",
"dismiss": "Приховати",
"gs_label": "Перші кроки — ці підказки зникнуть у міру зростання налаштування",
"gs_setups_chip": "Рекомендовані налаштування: знайдено {n} пристроїв із готовими тригерами",
@@ -836,7 +836,6 @@
"update_reload": "重新加载",
"battery_fleet_forecast_overdue": "预测日期已过——电池仍报告状态良好。如果您已更换电池,请记录更换;否则说明预测有误。",
"cost_from_parts": "采用配件合计 ≈ {amount}",
"new_menu": "新建",
"dismiss": "忽略",
"gs_label": "入门提示——随着配置的完善,这些提示会自动消失",
"gs_setups_chip": "推荐配置发现 {n} 台设备(含预设触发器)",
@@ -1,7 +1,7 @@
/** Maintenance Supporter Lovelace Card. */
import { LitElement, html, css, nothing } from "lit";
import { mergeSubscriptionEvent, type SubscriptionEvent } from "./helpers/subscription-merge";
import { applySubscriptionEvent, type SubscriptionEvent } from "./helpers/subscription-merge";
import { hydrateObjects } from "./helpers/hydrate-objects";
import { property, state } from "lit/decorators.js";
import { sharedStyles, STATUS_COLORS, t, ensureLocale, isLocaleLoaded, setDateTimePrefs, formatDueDays } from "./styles";
@@ -276,11 +276,10 @@ export class MaintenanceSupporterCard extends LitElement {
try {
const unsub = await this.hass.connection.subscribeMessage(
(msg: unknown) => {
const ev = msg as SubscriptionEvent<MaintenanceObjectResponse>;
// Compact payloads: hydrate before merging (helpers/hydrate-objects).
if (ev.objects) hydrateObjects(ev.objects);
if (ev.delta) hydrateObjects(ev.delta);
const next = mergeSubscriptionEvent(this._objects, ev);
const next = applySubscriptionEvent(
this._objects,
msg as SubscriptionEvent<MaintenanceObjectResponse>,
);
if (next !== null) this._objects = next;
},
// deltas: only changed entries arrive — see helpers/subscription-merge.
@@ -2,7 +2,7 @@
import { LitElement, html, nothing } from "lit";
import { isSafeHttpUrl } from "./helpers/url";
import { mergeSubscriptionEvent, type SubscriptionEvent } from "./helpers/subscription-merge";
import { applySubscriptionEvent, type SubscriptionEvent } from "./helpers/subscription-merge";
import { isStaleBundle } from "./helpers/bundle-version";
import { customElement, property, state } from "lit/decorators.js";
import { sharedStyles, STATUS_COLORS, STATUS_ICONS, DEFAULT_CURRENCY_SYMBOL, t, ensureLocale, isLocaleLoaded, formatDate, formatDueDays, formatInterval, formatRecurrence, setDateTimePrefs } from "./styles";
@@ -657,11 +657,7 @@ export class MaintenanceSupporterPanel extends LitElement {
const unsub = await this.hass.connection.subscribeMessage(
(msg: unknown) => {
const ev = msg as SubscriptionEvent<MaintenanceObjectResponse>;
// Compact payloads: hydrate incoming entries before merging so
// everything downstream keeps seeing the full shape.
if (ev.objects) hydrateObjects(ev.objects);
if (ev.delta) hydrateObjects(ev.delta);
const next = mergeSubscriptionEvent(this._objects, ev);
const next = applySubscriptionEvent(this._objects, ev);
if (next !== null) {
this._objects = next;
// Full snapshots are rare (subscribe start) — keep the skeleton
@@ -3284,7 +3280,7 @@ export class MaintenanceSupporterPanel extends LitElement {
<div class="new-menu-wrapper">
<ha-button appearance="filled" class="new-menu-button"
@click=${(e: Event) => { e.stopPropagation(); this._toggleNewMenu(); }}>
<ha-icon icon="mdi:plus"></ha-icon> ${t("new_menu", L)}
<ha-icon icon="mdi:plus"></ha-icon> ${t("add", L)}
<ha-icon icon="mdi:menu-down"></ha-icon>
</ha-button>
${this._newMenuOpen ? html`
@@ -3316,16 +3312,25 @@ export class MaintenanceSupporterPanel extends LitElement {
`;
}
private _toggleNewMenu(): void {
this._newMenuOpen = !this._newMenuOpen;
if (this._newMenuOpen) {
/** Shared popup mechanic (DRY audit 2026-08: this closer was copy-pasted
* three times): flip the flag, and while open arm a one-shot document
* click that closes it again. The setTimeout defers past the click that
* opened the menu. */
private _togglePopup(get: () => boolean, set: (v: boolean) => void): void {
const open = !get();
set(open);
if (open) {
setTimeout(() => {
const handler = () => { this._newMenuOpen = false; document.removeEventListener("click", handler); };
const handler = () => { set(false); document.removeEventListener("click", handler); };
document.addEventListener("click", handler);
}, 0);
}
}
private _toggleNewMenu(): void {
this._togglePopup(() => this._newMenuOpen, (v) => { this._newMenuOpen = v; });
}
private _closeNewMenu(): void {
this._newMenuOpen = false;
}
@@ -3421,13 +3426,7 @@ export class MaintenanceSupporterPanel extends LitElement {
}
private _toggleObjMenu(): void {
this._objMenuOpen = !this._objMenuOpen;
if (this._objMenuOpen) {
setTimeout(() => {
const handler = () => { this._objMenuOpen = false; document.removeEventListener("click", handler); };
document.addEventListener("click", handler);
}, 0);
}
this._togglePopup(() => this._objMenuOpen, (v) => { this._objMenuOpen = v; });
}
private _closeObjMenu(): void {
@@ -3435,12 +3434,7 @@ export class MaintenanceSupporterPanel extends LitElement {
}
private _toggleMoreMenu(): void {
this._moreMenuOpen = !this._moreMenuOpen;
if (this._moreMenuOpen) {
// Close menu on next outside click
const handler = () => { this._moreMenuOpen = false; document.removeEventListener("click", handler); };
setTimeout(() => document.addEventListener("click", handler, { once: true }), 0);
}
this._togglePopup(() => this._moreMenuOpen, (v) => { this._moreMenuOpen = v; });
}
private _closeMoreMenu(): void {