91 files
This commit is contained in:
+85
@@ -19,6 +19,9 @@ interface BatteryRow {
|
||||
days_until: number | null;
|
||||
available?: boolean;
|
||||
}
|
||||
interface RosterRow extends BatteryRow {
|
||||
status: "low" | "soon" | "ok";
|
||||
}
|
||||
interface Overview {
|
||||
available: boolean;
|
||||
configured: boolean;
|
||||
@@ -26,6 +29,9 @@ interface Overview {
|
||||
total: number;
|
||||
low: BatteryRow[];
|
||||
soon: BatteryRow[];
|
||||
// Every tracked battery, whatever its state. Optional so an older backend
|
||||
// simply renders no roster instead of throwing.
|
||||
all?: RosterRow[];
|
||||
needs_now: Record<string, number>;
|
||||
needs_soon: Record<string, number>;
|
||||
types: string[];
|
||||
@@ -133,6 +139,16 @@ export class MaintenanceBatteryFleetSection extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
/** The forecast as a date a person can plan with, not a day count.
|
||||
* `days_until` comes from last-replaced + typical lifetime, so it is an
|
||||
* estimate — the tilde in the template says so. Negative values (past the
|
||||
* typical lifetime but not reported low yet) render as past dates, which
|
||||
* is honest: the battery is living on borrowed time. */
|
||||
private _predictedDate(daysUntil: number): string {
|
||||
const when = new Date(Date.now() + daysUntil * 864e5);
|
||||
return new Intl.DateTimeFormat(this._lang, { day: "numeric", month: "numeric", year: "numeric" }).format(when);
|
||||
}
|
||||
|
||||
private _shoppingLine(needs: Record<string, number>): string {
|
||||
return Object.entries(needs)
|
||||
.map(([type, qty]) => `${qty}× ${type}`)
|
||||
@@ -220,6 +236,41 @@ export class MaintenanceBatteryFleetSection extends LitElement {
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
${ov.all?.length
|
||||
? html`
|
||||
<details class="bf-roster">
|
||||
<summary>${t("battery_fleet_all", L)} (${ov.all.length})</summary>
|
||||
<div class="bf-rows">
|
||||
${ov.all.map(
|
||||
(b) => html`
|
||||
<div class="bf-row">
|
||||
<span class="bf-dev">${b.device_name}</span>
|
||||
<span class="bf-status bf-${b.status}">${t("battery_fleet_status_" + b.status, L)}</span>
|
||||
<span class="bf-type">${b.quantity}× ${b.battery_type}</span>
|
||||
${b.level != null ? html`<span class="bf-level">${b.level}%</span>` : nothing}
|
||||
${b.days_until != null
|
||||
? html`<span
|
||||
class="bf-predicted"
|
||||
title=${t("battery_fleet_predicted_on", L).replace("{date}", this._predictedDate(b.days_until))}
|
||||
>~${this._predictedDate(b.days_until)}</span
|
||||
>`
|
||||
: nothing}
|
||||
<button
|
||||
class="bf-mark bf-exclude"
|
||||
title=${t("battery_fleet_exclude", L)}
|
||||
.disabled=${this._marking}
|
||||
@click=${() => this._setExcluded(b.entity_id, true)}
|
||||
>
|
||||
<ha-icon icon="mdi:eye-off-outline"></ha-icon>
|
||||
</button>
|
||||
</div>
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
<div class="bf-roster-hint">${t("battery_fleet_all_hint", L)}</div>
|
||||
</details>
|
||||
`
|
||||
: nothing}
|
||||
${ov.excluded?.length
|
||||
? html`
|
||||
<div class="bf-excluded">
|
||||
@@ -363,6 +414,40 @@ export class MaintenanceBatteryFleetSection extends LitElement {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
/* The roster is a lookup list, not the headline — collapsed by default so
|
||||
the section still opens on what actually needs doing. */
|
||||
.bf-roster > summary {
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
padding: 2px 0;
|
||||
}
|
||||
.bf-roster-hint {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
padding-top: 6px;
|
||||
}
|
||||
.bf-status {
|
||||
font-size: 11px;
|
||||
padding: 1px 7px;
|
||||
border-radius: 9px;
|
||||
white-space: nowrap;
|
||||
background: var(--secondary-background-color, rgba(127, 127, 127, 0.15));
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.bf-status.bf-low {
|
||||
background: var(--error-color, #f44336);
|
||||
color: #fff;
|
||||
}
|
||||
.bf-status.bf-soon {
|
||||
background: var(--warning-color, #ff9800);
|
||||
color: #fff;
|
||||
}
|
||||
.bf-predicted {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.bf-total {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Vyloučit z flotily",
|
||||
"battery_fleet_excluded": "Vyloučené",
|
||||
"battery_fleet_include": "Znovu sledovat",
|
||||
"battery_fleet_all": "Všechny sledované baterie",
|
||||
"battery_fleet_all_hint": "Vylučte zařízení už dřív, než vůbec ohlásí nízkou úroveň — vysavač, který se sám nabíjí, nebo telefon, který varuje sám.",
|
||||
"battery_fleet_status_low": "Nízká",
|
||||
"battery_fleet_status_soon": "Brzy",
|
||||
"battery_fleet_status_ok": "V pořádku",
|
||||
"battery_fleet_predicted_on": "Očekáváno kolem {date}",
|
||||
"battery_fleet_total": "Sledováno baterií: {n}",
|
||||
"battery_fleet_setup_button": "Flotila baterií",
|
||||
"battery_fleet_setup_done": "Flotila baterií nastavena — jeden úkol sleduje všechny.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Udeluk fra flåden",
|
||||
"battery_fleet_excluded": "Udelukket",
|
||||
"battery_fleet_include": "Følg igen",
|
||||
"battery_fleet_all": "Alle overvågede batterier",
|
||||
"battery_fleet_all_hint": "Udeluk en enhed her, før den overhovedet melder lavt niveau — en støvsuger, der lader sig selv, eller en telefon, der advarer af sig selv.",
|
||||
"battery_fleet_status_low": "Lav",
|
||||
"battery_fleet_status_soon": "Snart",
|
||||
"battery_fleet_status_ok": "I orden",
|
||||
"battery_fleet_predicted_on": "Forventes omkring {date}",
|
||||
"battery_fleet_total": "{n} batterier overvåges",
|
||||
"battery_fleet_setup_button": "Batteriflåde",
|
||||
"battery_fleet_setup_done": "Batteriflåde opsat — én opgave følger alle batterier.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Aus der Flotte ausschließen",
|
||||
"battery_fleet_excluded": "Ausgeschlossen",
|
||||
"battery_fleet_include": "Wieder aufnehmen",
|
||||
"battery_fleet_all": "Alle überwachten Batterien",
|
||||
"battery_fleet_all_hint": "Ein Gerät hier ausschließen, bevor es überhaupt schwach meldet — etwa ein Sauger, der sich selbst lädt, oder ein Telefon, das von sich aus warnt.",
|
||||
"battery_fleet_status_low": "Schwach",
|
||||
"battery_fleet_status_soon": "Bald",
|
||||
"battery_fleet_status_ok": "In Ordnung",
|
||||
"battery_fleet_predicted_on": "Voraussichtlich um {date}",
|
||||
"battery_fleet_total": "{n} Batterien überwacht",
|
||||
"battery_fleet_setup_button": "Batterie-Flotte",
|
||||
"battery_fleet_setup_done": "Batterie-Flotte eingerichtet — ein Task überwacht alle Batterien.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Exclude from the fleet",
|
||||
"battery_fleet_excluded": "Excluded",
|
||||
"battery_fleet_include": "Track again",
|
||||
"battery_fleet_all": "All tracked batteries",
|
||||
"battery_fleet_all_hint": "Exclude a device here to drop it from the fleet before it ever reports low — a vacuum that recharges itself, or a phone that warns you on its own.",
|
||||
"battery_fleet_status_low": "Low",
|
||||
"battery_fleet_status_soon": "Soon",
|
||||
"battery_fleet_status_ok": "Healthy",
|
||||
"battery_fleet_predicted_on": "Expected around {date}",
|
||||
"battery_fleet_total": "{n} batteries tracked",
|
||||
"battery_fleet_setup_button": "Battery fleet",
|
||||
"battery_fleet_setup_done": "Battery fleet set up — one task tracks all your batteries.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Excluir de la flota",
|
||||
"battery_fleet_excluded": "Excluidas",
|
||||
"battery_fleet_include": "Seguir de nuevo",
|
||||
"battery_fleet_all": "Todas las pilas monitorizadas",
|
||||
"battery_fleet_all_hint": "Excluye aquí un dispositivo antes de que llegue a avisar de carga baja — un aspirador que se recarga solo o un teléfono que ya avisa por su cuenta.",
|
||||
"battery_fleet_status_low": "Baja",
|
||||
"battery_fleet_status_soon": "Pronto",
|
||||
"battery_fleet_status_ok": "Correcta",
|
||||
"battery_fleet_predicted_on": "Previsto hacia {date}",
|
||||
"battery_fleet_total": "{n} baterías monitorizadas",
|
||||
"battery_fleet_setup_button": "Flota de baterías",
|
||||
"battery_fleet_setup_done": "Flota de baterías configurada — una tarea controla todas.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Sulje pois kannasta",
|
||||
"battery_fleet_excluded": "Pois suljetut",
|
||||
"battery_fleet_include": "Seuraa uudelleen",
|
||||
"battery_fleet_all": "Kaikki seuratut paristot",
|
||||
"battery_fleet_all_hint": "Sulje laite pois jo ennen kuin se ilmoittaa alhaisesta varauksesta — imuri, joka lataa itsensä, tai puhelin, joka varoittaa itse.",
|
||||
"battery_fleet_status_low": "Vähissä",
|
||||
"battery_fleet_status_soon": "Pian",
|
||||
"battery_fleet_status_ok": "Kunnossa",
|
||||
"battery_fleet_predicted_on": "Odotettavissa noin {date}",
|
||||
"battery_fleet_total": "{n} paristoa seurannassa",
|
||||
"battery_fleet_setup_button": "Paristokanta",
|
||||
"battery_fleet_setup_done": "Paristokanta määritetty — yksi tehtävä seuraa kaikkia.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Exclure du parc",
|
||||
"battery_fleet_excluded": "Exclues",
|
||||
"battery_fleet_include": "Suivre à nouveau",
|
||||
"battery_fleet_all": "Toutes les piles suivies",
|
||||
"battery_fleet_all_hint": "Excluez un appareil ici avant même qu'il ne signale une charge faible — un aspirateur qui se recharge seul, ou un téléphone qui vous prévient lui-même.",
|
||||
"battery_fleet_status_low": "Faible",
|
||||
"battery_fleet_status_soon": "Bientôt",
|
||||
"battery_fleet_status_ok": "Bon état",
|
||||
"battery_fleet_predicted_on": "Prévu vers {date}",
|
||||
"battery_fleet_total": "{n} piles suivies",
|
||||
"battery_fleet_setup_button": "Parc de piles",
|
||||
"battery_fleet_setup_done": "Parc de piles configuré — une tâche suit toutes vos piles.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "फ्लीट से बाहर करें",
|
||||
"battery_fleet_excluded": "बाहर किए गए",
|
||||
"battery_fleet_include": "फिर से ट्रैक करें",
|
||||
"battery_fleet_all": "सभी ट्रैक की गई बैटरियाँ",
|
||||
"battery_fleet_all_hint": "किसी डिवाइस को यहाँ कम बैटरी बताने से पहले ही बाहर रखें — जैसे खुद चार्ज होने वाला वैक्यूम, या खुद ही चेतावनी देने वाला फ़ोन।",
|
||||
"battery_fleet_status_low": "कम",
|
||||
"battery_fleet_status_soon": "जल्द",
|
||||
"battery_fleet_status_ok": "ठीक",
|
||||
"battery_fleet_predicted_on": "{date} के आसपास अपेक्षित",
|
||||
"battery_fleet_total": "{n} बैटरियाँ ट्रैक की गईं",
|
||||
"battery_fleet_setup_button": "बैटरी फ्लीट",
|
||||
"battery_fleet_setup_done": "बैटरी फ्लीट सेट — एक कार्य सभी बैटरियों को ट्रैक करता है।",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Kizárás a flottából",
|
||||
"battery_fleet_excluded": "Kizárva",
|
||||
"battery_fleet_include": "Követés újra",
|
||||
"battery_fleet_all": "Minden nyomon követett elem",
|
||||
"battery_fleet_all_hint": "Zárjon ki egy eszközt már azelőtt, hogy alacsony töltöttséget jelezne — például egy magát töltő porszívót vagy egy telefont, amely magától figyelmeztet.",
|
||||
"battery_fleet_status_low": "Alacsony",
|
||||
"battery_fleet_status_soon": "Hamarosan",
|
||||
"battery_fleet_status_ok": "Rendben",
|
||||
"battery_fleet_predicted_on": "Várhatóan {date} körül",
|
||||
"battery_fleet_total": "{n} elem követve",
|
||||
"battery_fleet_setup_button": "Elemflotta",
|
||||
"battery_fleet_setup_done": "Elemflotta beállítva — egyetlen feladat követi az összes elemet.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Escludi dal parco",
|
||||
"battery_fleet_excluded": "Escluse",
|
||||
"battery_fleet_include": "Traccia di nuovo",
|
||||
"battery_fleet_all": "Tutte le batterie monitorate",
|
||||
"battery_fleet_all_hint": "Escludi qui un dispositivo prima ancora che segnali la batteria scarica — un aspirapolvere che si ricarica da solo o un telefono che avvisa da sé.",
|
||||
"battery_fleet_status_low": "Scarica",
|
||||
"battery_fleet_status_soon": "A breve",
|
||||
"battery_fleet_status_ok": "A posto",
|
||||
"battery_fleet_predicted_on": "Previsto intorno al {date}",
|
||||
"battery_fleet_total": "{n} batterie monitorate",
|
||||
"battery_fleet_setup_button": "Parco batterie",
|
||||
"battery_fleet_setup_done": "Parco batterie configurato — un'attività monitora tutte.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "フリートから除外",
|
||||
"battery_fleet_excluded": "除外済み",
|
||||
"battery_fleet_include": "再び追跡",
|
||||
"battery_fleet_all": "追跡中のすべての電池",
|
||||
"battery_fleet_all_hint": "電池残量が少ないと報告される前に、ここで機器を除外できます — 自動充電するロボット掃除機や、自分で警告するスマートフォンなど。",
|
||||
"battery_fleet_status_low": "残量低下",
|
||||
"battery_fleet_status_soon": "まもなく",
|
||||
"battery_fleet_status_ok": "良好",
|
||||
"battery_fleet_predicted_on": "{date} 頃の見込み",
|
||||
"battery_fleet_total": "{n} 個の電池を監視",
|
||||
"battery_fleet_setup_button": "電池フリート",
|
||||
"battery_fleet_setup_done": "電池フリートを設定 — 1つのタスクで全電池を管理。",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "플릿에서 제외",
|
||||
"battery_fleet_excluded": "제외됨",
|
||||
"battery_fleet_include": "다시 추적",
|
||||
"battery_fleet_all": "추적 중인 모든 배터리",
|
||||
"battery_fleet_all_hint": "배터리 부족을 알리기 전에 여기에서 기기를 제외하세요 — 스스로 충전하는 로봇청소기나 자체적으로 경고하는 휴대폰처럼.",
|
||||
"battery_fleet_status_low": "부족",
|
||||
"battery_fleet_status_soon": "곧",
|
||||
"battery_fleet_status_ok": "정상",
|
||||
"battery_fleet_predicted_on": "{date}쯤 예상",
|
||||
"battery_fleet_total": "배터리 {n}개 추적 중",
|
||||
"battery_fleet_setup_button": "배터리 플릿",
|
||||
"battery_fleet_setup_done": "배터리 플릿이 설정되었습니다 — 하나의 작업이 모든 배터리를 추적합니다.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Utelukk fra flåten",
|
||||
"battery_fleet_excluded": "Utelukket",
|
||||
"battery_fleet_include": "Følg igjen",
|
||||
"battery_fleet_all": "Alle overvåkede batterier",
|
||||
"battery_fleet_all_hint": "Utelukk en enhet her før den i det hele tatt melder lavt nivå — en støvsuger som lader seg selv, eller en telefon som varsler av seg selv.",
|
||||
"battery_fleet_status_low": "Lav",
|
||||
"battery_fleet_status_soon": "Snart",
|
||||
"battery_fleet_status_ok": "I orden",
|
||||
"battery_fleet_predicted_on": "Forventes rundt {date}",
|
||||
"battery_fleet_total": "{n} batterier spores",
|
||||
"battery_fleet_setup_button": "Batteriflåte",
|
||||
"battery_fleet_setup_done": "Batteriflåte satt opp — én oppgave følger alle batteriene.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Uitsluiten van de vloot",
|
||||
"battery_fleet_excluded": "Uitgesloten",
|
||||
"battery_fleet_include": "Opnieuw volgen",
|
||||
"battery_fleet_all": "Alle gevolgde batterijen",
|
||||
"battery_fleet_all_hint": "Sluit een apparaat hier uit voordat het ooit laag meldt — een stofzuiger die zichzelf oplaadt, of een telefoon die zelf waarschuwt.",
|
||||
"battery_fleet_status_low": "Laag",
|
||||
"battery_fleet_status_soon": "Binnenkort",
|
||||
"battery_fleet_status_ok": "In orde",
|
||||
"battery_fleet_predicted_on": "Verwacht rond {date}",
|
||||
"battery_fleet_total": "{n} batterijen gevolgd",
|
||||
"battery_fleet_setup_button": "Batterijvloot",
|
||||
"battery_fleet_setup_done": "Batterijvloot ingesteld — één taak volgt al je batterijen.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Wyklucz z floty",
|
||||
"battery_fleet_excluded": "Wykluczone",
|
||||
"battery_fleet_include": "Śledź ponownie",
|
||||
"battery_fleet_all": "Wszystkie śledzone baterie",
|
||||
"battery_fleet_all_hint": "Wyklucz tutaj urządzenie, zanim w ogóle zgłosi niski poziom — odkurzacz, który sam się ładuje, albo telefon, który ostrzega samodzielnie.",
|
||||
"battery_fleet_status_low": "Niski",
|
||||
"battery_fleet_status_soon": "Wkrótce",
|
||||
"battery_fleet_status_ok": "W porządku",
|
||||
"battery_fleet_predicted_on": "Przewidywane około {date}",
|
||||
"battery_fleet_total": "Śledzone baterie: {n}",
|
||||
"battery_fleet_setup_button": "Flota baterii",
|
||||
"battery_fleet_setup_done": "Flota baterii skonfigurowana — jedno zadanie śledzi wszystkie.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Remover da frota",
|
||||
"battery_fleet_excluded": "Removida",
|
||||
"battery_fleet_include": "Acompanhar novamente",
|
||||
"battery_fleet_all": "Todas as baterias monitoradas",
|
||||
"battery_fleet_all_hint": "Exclua aqui um dispositivo antes que ele sequer indique carga baixa — um aspirador que se recarrega sozinho ou um celular que já avisa sozinho.",
|
||||
"battery_fleet_status_low": "Fraca",
|
||||
"battery_fleet_status_soon": "Em breve",
|
||||
"battery_fleet_status_ok": "Em bom estado",
|
||||
"battery_fleet_predicted_on": "Previsto por volta de {date}",
|
||||
"battery_fleet_total": "{n} baterias acompanhadas",
|
||||
"battery_fleet_setup_button": "Frota de baterias",
|
||||
"battery_fleet_setup_done": "Frota de baterias configurada — uma única tarefa acompanha todas as suas baterias.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Excluir da frota",
|
||||
"battery_fleet_excluded": "Excluídas",
|
||||
"battery_fleet_include": "Acompanhar novamente",
|
||||
"battery_fleet_all": "Todas as pilhas monitorizadas",
|
||||
"battery_fleet_all_hint": "Exclua aqui um dispositivo antes de ele sequer indicar carga fraca — um aspirador que se recarrega sozinho ou um telemóvel que já avisa por si.",
|
||||
"battery_fleet_status_low": "Fraca",
|
||||
"battery_fleet_status_soon": "Em breve",
|
||||
"battery_fleet_status_ok": "Em bom estado",
|
||||
"battery_fleet_predicted_on": "Previsto por volta de {date}",
|
||||
"battery_fleet_total": "{n} baterias monitorizadas",
|
||||
"battery_fleet_setup_button": "Frota de baterias",
|
||||
"battery_fleet_setup_done": "Frota de baterias configurada — uma tarefa acompanha todas.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Исключить из парка",
|
||||
"battery_fleet_excluded": "Исключены",
|
||||
"battery_fleet_include": "Снова отслеживать",
|
||||
"battery_fleet_all": "Все отслеживаемые батареи",
|
||||
"battery_fleet_all_hint": "Исключите устройство здесь ещё до того, как оно сообщит о низком заряде — робот-пылесос, который заряжается сам, или телефон, который предупреждает вас сам.",
|
||||
"battery_fleet_status_low": "Низкий",
|
||||
"battery_fleet_status_soon": "Скоро",
|
||||
"battery_fleet_status_ok": "В норме",
|
||||
"battery_fleet_predicted_on": "Ожидается примерно {date}",
|
||||
"battery_fleet_total": "Отслеживается батарей: {n}",
|
||||
"battery_fleet_setup_button": "Парк батарей",
|
||||
"battery_fleet_setup_done": "Парк батарей настроен — одна задача следит за всеми.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Uteslut från flottan",
|
||||
"battery_fleet_excluded": "Uteslutna",
|
||||
"battery_fleet_include": "Spåra igen",
|
||||
"battery_fleet_all": "Alla bevakade batterier",
|
||||
"battery_fleet_all_hint": "Uteslut en enhet här redan innan den rapporterar låg nivå — en dammsugare som laddar sig själv, eller en telefon som varnar på egen hand.",
|
||||
"battery_fleet_status_low": "Låg",
|
||||
"battery_fleet_status_soon": "Snart",
|
||||
"battery_fleet_status_ok": "I ordning",
|
||||
"battery_fleet_predicted_on": "Väntas omkring {date}",
|
||||
"battery_fleet_total": "{n} batterier spåras",
|
||||
"battery_fleet_setup_button": "Batteriflotta",
|
||||
"battery_fleet_setup_done": "Batteriflotta konfigurerad — en uppgift följer alla batterier.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Filodan hariç tut",
|
||||
"battery_fleet_excluded": "Hariç tutuldu",
|
||||
"battery_fleet_include": "Yeniden takip et",
|
||||
"battery_fleet_all": "İzlenen tüm piller",
|
||||
"battery_fleet_all_hint": "Bir cihazı, daha düşük şarj bildirmeden burada hariç tutun — kendi kendine şarj olan bir süpürge ya da zaten kendisi uyaran bir telefon gibi.",
|
||||
"battery_fleet_status_low": "Düşük",
|
||||
"battery_fleet_status_soon": "Yakında",
|
||||
"battery_fleet_status_ok": "İyi durumda",
|
||||
"battery_fleet_predicted_on": "Yaklaşık {date} tarihinde bekleniyor",
|
||||
"battery_fleet_total": "{n} pil takip ediliyor",
|
||||
"battery_fleet_setup_button": "Pil filosu",
|
||||
"battery_fleet_setup_done": "Pil filosu kuruldu — tek bir görev tüm pillerinizi takip ediyor.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "Виключити з парку",
|
||||
"battery_fleet_excluded": "Виключені",
|
||||
"battery_fleet_include": "Знову відстежувати",
|
||||
"battery_fleet_all": "Усі відстежувані батареї",
|
||||
"battery_fleet_all_hint": "Вилучіть пристрій тут ще до того, як він повідомить про низький заряд — робот-пилосос, що заряджається сам, або телефон, який попереджає сам.",
|
||||
"battery_fleet_status_low": "Низький",
|
||||
"battery_fleet_status_soon": "Незабаром",
|
||||
"battery_fleet_status_ok": "У нормі",
|
||||
"battery_fleet_predicted_on": "Очікується приблизно {date}",
|
||||
"battery_fleet_total": "Відстежується батарей: {n}",
|
||||
"battery_fleet_setup_button": "Парк батарей",
|
||||
"battery_fleet_setup_done": "Парк батарей налаштовано — одне завдання стежить за всіма.",
|
||||
|
||||
@@ -814,6 +814,12 @@
|
||||
"battery_fleet_exclude": "从电池群中排除",
|
||||
"battery_fleet_excluded": "已排除",
|
||||
"battery_fleet_include": "重新跟踪",
|
||||
"battery_fleet_all": "所有跟踪的电池",
|
||||
"battery_fleet_all_hint": "在设备报告电量不足之前就可以在这里将其排除——例如会自行充电的扫地机器人,或本身就会提醒你的手机。",
|
||||
"battery_fleet_status_low": "电量低",
|
||||
"battery_fleet_status_soon": "即将",
|
||||
"battery_fleet_status_ok": "正常",
|
||||
"battery_fleet_predicted_on": "预计在 {date} 前后",
|
||||
"battery_fleet_total": "已跟踪 {n} 个电池",
|
||||
"battery_fleet_setup_button": "电池群",
|
||||
"battery_fleet_setup_done": "电池群已设置 — 一个任务跟踪所有电池。",
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
* show_user_filter: true # default true
|
||||
* user_filter: "" # "" | "current_user" | "<uuid>"
|
||||
* show_object_filter: true # default true; dropdown appears with 2+ objects
|
||||
* object_filter: "" # "" | "<entry_id>" | "<object name>" (Discussion #83)
|
||||
* object_filter: "" # "" | "<entry_id>" | "<object name>" — or a LIST of
|
||||
* # them to restrict the card to several objects; the
|
||||
* # dropdown then narrows within that set (Discussion #83)
|
||||
*/
|
||||
|
||||
import { LitElement, html, css, nothing } from "lit";
|
||||
@@ -51,7 +53,7 @@ interface CalendarCardConfig {
|
||||
user_filter?: string;
|
||||
/** Discussion #83 — in-card object dropdown + optional preselect. */
|
||||
show_object_filter?: boolean;
|
||||
object_filter?: string;
|
||||
object_filter?: string | string[];
|
||||
/** v2.2.0 — when set, the card renders past N days from history instead
|
||||
* of forward N days from next_due. Mutually exclusive with window_days. */
|
||||
past_days?: PastDays;
|
||||
@@ -68,6 +70,8 @@ export class MaintenanceCalendarCard extends LitElement {
|
||||
@state() private _pastDays: PastDays | 0 = 0; // 0 = forward mode
|
||||
@state() private _userFilter = "";
|
||||
@state() private _objectFilter = "";
|
||||
// 2+ configured object_filter values — restricts the card to this set.
|
||||
private _configuredObjects: string[] = [];
|
||||
@state() private _unsub: (() => void) | null = null;
|
||||
|
||||
private _dataLoaded = false;
|
||||
@@ -100,6 +104,15 @@ export class MaintenanceCalendarCard extends LitElement {
|
||||
}
|
||||
if (typeof config.object_filter === "string") {
|
||||
this._objectFilter = config.object_filter;
|
||||
this._configuredObjects = [];
|
||||
} else if (Array.isArray(config.object_filter)) {
|
||||
const values = config.object_filter.filter((v): v is string => typeof v === "string" && v !== "");
|
||||
// One entry behaves exactly like the string form (pre-select, dropdown
|
||||
// over all objects); two or more RESTRICT the card to that set and the
|
||||
// dropdown narrows within it — the multi-object parity with the task
|
||||
// card's filter_objects that #83 asked for.
|
||||
this._objectFilter = values.length === 1 ? values[0] : "";
|
||||
this._configuredObjects = values.length > 1 ? values : [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,16 +249,28 @@ export class MaintenanceCalendarCard extends LitElement {
|
||||
// Object filter (#83): the config value may be an entry_id or an object
|
||||
// NAME (friendlier in hand-written YAML) — resolve against the loaded
|
||||
// objects; an unknown value falls back to "all" instead of a blank card.
|
||||
const showObjectFilter = this._config.show_object_filter !== false && this._objects.length > 1;
|
||||
let filterEntryId: string | null = null;
|
||||
if (this._objectFilter) {
|
||||
const needle = this._objectFilter.toLowerCase();
|
||||
const resolve = (value: string): string | null => {
|
||||
const needle = value.toLowerCase();
|
||||
const match = this._objects.find(
|
||||
(o) => o.entry_id === this._objectFilter || o.object.name.toLowerCase() === needle,
|
||||
(o) => o.entry_id === value || o.object.name.toLowerCase() === needle,
|
||||
);
|
||||
filterEntryId = match?.entry_id ?? null;
|
||||
}
|
||||
const objects = filterEntryId ? this._objects.filter((o) => o.entry_id === filterEntryId) : this._objects;
|
||||
return match?.entry_id ?? null;
|
||||
};
|
||||
// A configured LIST restricts the card to that set; unknown names resolve
|
||||
// to nothing and an entirely-unresolved list falls back to all objects
|
||||
// rather than a blank card (same rule the single value always had).
|
||||
const configuredIds = new Set(
|
||||
this._configuredObjects.map(resolve).filter((id): id is string => id !== null),
|
||||
);
|
||||
const base = configuredIds.size
|
||||
? this._objects.filter((o) => configuredIds.has(o.entry_id))
|
||||
: this._objects;
|
||||
const showObjectFilter = this._config.show_object_filter !== false && base.length > 1;
|
||||
const filterEntryId = this._objectFilter ? resolve(this._objectFilter) : null;
|
||||
const objects =
|
||||
filterEntryId && base.some((o) => o.entry_id === filterEntryId)
|
||||
? base.filter((o) => o.entry_id === filterEntryId)
|
||||
: base;
|
||||
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
@@ -394,7 +419,7 @@ export class MaintenanceCalendarCard extends LitElement {
|
||||
this._objectFilter = (e.target as HTMLSelectElement).value;
|
||||
}}>
|
||||
<option value="">${t("all_objects", L)}</option>
|
||||
${[...this._objects]
|
||||
${[...base]
|
||||
.sort((a, b) => a.object.name.localeCompare(b.object.name))
|
||||
.map(
|
||||
(o) => html`<option value=${o.entry_id} ?selected=${o.entry_id === filterEntryId}>${o.object.name}</option>`,
|
||||
@@ -569,7 +594,8 @@ class MaintenanceCalendarCardEditor extends LitElement {
|
||||
/>
|
||||
</div>
|
||||
<div class="hint">
|
||||
Pre-select one object via YAML: object_filter: "<object name>".
|
||||
Pre-select one object via YAML: object_filter: "<object name>" — or a
|
||||
list of names to restrict the card to several objects.
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user