361 files

This commit is contained in:
Home Assistant Version Control
2026-10-06 17:11:43 +00:00
parent d2c5af43e3
commit bf886c9347
362 changed files with 22060 additions and 1285 deletions
@@ -42,12 +42,13 @@ export interface MockFeatures {
checklists: boolean;
schedule_time: boolean;
completion_actions: boolean;
places: boolean;
}
export const DEFAULT_FEATURES: MockFeatures = {
adaptive: false, seasonal: false,
environmental: false, budget: false, groups: false,
checklists: false, schedule_time: false, completion_actions: false,
checklists: false, schedule_time: false, completion_actions: false, places: false,
};
/**
@@ -69,7 +69,7 @@ function ctx(overrides: Partial<TaskDetailContext> = {}): TaskDetailContext {
features: {
adaptive: false, seasonal: false,
environmental: false, budget: false, groups: false,
checklists: false, schedule_time: false, completion_actions: false,
checklists: false, schedule_time: false, completion_actions: false, places: false,
},
currencySymbol: "€",
collapsedSections: new Set(),
@@ -297,7 +297,7 @@ describe("task-detail renderer", () => {
features: {
adaptive: false, seasonal: false,
environmental: false, budget: false, groups: false,
checklists: true, schedule_time: false, completion_actions: false,
checklists: true, schedule_time: false, completion_actions: false, places: false,
},
setChecklistItem: (item, done) => calls.push([item, done]),
}),
@@ -66,7 +66,7 @@ function ctx(overrides: Partial<TaskDetailContext> = {}): TaskDetailContext {
features: {
adaptive: false, seasonal: false,
environmental: false, budget: false, groups: false,
checklists: false, schedule_time: false, completion_actions: false,
checklists: false, schedule_time: false, completion_actions: false, places: false,
},
currencySymbol: "€",
collapsedSections: new Set(),
@@ -21,6 +21,8 @@ export class MaintenanceObjectDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
/** All objects — choices for the parent-object picker (2.19). */
@property({ attribute: false }) public objects: MaintenanceObjectResponse[] = [];
/** 2026-10 places: the advanced feature switch shows the place fields. */
@property({ type: Boolean }) public placesEnabled = false;
@state() private _open = false;
@state() private _loading = false;
@state() private _error = "";
@@ -39,6 +41,9 @@ export class MaintenanceObjectDialog extends LitElement {
// 2.19: attach to an existing HA device / nest under another object
@state() private _haDeviceId = "";
@state() private _parentEntryId = "";
// 2026-10 places: an HA zone ("" = home) and "remind only on site".
@state() private _place = "";
@state() private _remindOnSite = false;
@state() private _entryId: string | null = null; // null = create, string = update
// Replace (journey N1): the successor's device. The old unit's device is
// what the object links to now; a new unit usually is a new device.
@@ -63,6 +68,8 @@ export class MaintenanceObjectDialog extends LitElement {
this._notes = "";
this._haDeviceId = "";
this._parentEntryId = "";
this._place = "";
this._remindOnSite = false;
this._replacing = false;
this._error = "";
this._open = true;
@@ -96,6 +103,8 @@ export class MaintenanceObjectDialog extends LitElement {
this._notes = obj.notes || "";
this._haDeviceId = obj.ha_device_id || "";
this._parentEntryId = obj.parent_entry_id || "";
this._place = obj.place || "";
this._remindOnSite = !!obj.remind_on_site;
this._replacing = false;
this._error = "";
this._open = true;
@@ -194,6 +203,35 @@ export class MaintenanceObjectDialog extends LitElement {
`;
}
/** 2026-10 places: the zone the object is maintained at ("" = home) and
* whether its reminders wait until somebody is there. */
private _renderPlace(L: string) {
const missing = !!this._place && !this.hass?.states?.[this._place];
return html`
<ha-form
.hass=${this.hass}
.data=${{ place: this._place || undefined }}
.schema=${[{ name: "place", selector: { entity: { domain: "zone" } } }]}
.computeLabel=${() => t("place_optional", L)}
@value-changed=${(e: CustomEvent) => {
this._place = ((e.detail.value as { place?: string })?.place as string) || "";
if (!this._place) this._remindOnSite = false;
}}
></ha-form>
${missing ? html`<div class="place-missing">${t("place_missing", L)}</div>` : nothing}
<label class="place-on-site ${this._place ? "" : "disabled"}">
<input
type="checkbox"
.checked=${this._remindOnSite}
?disabled=${!this._place}
@change=${(e: Event) => (this._remindOnSite = (e.target as HTMLInputElement).checked)}
/>
<span>${t("remind_on_site", L)}</span>
</label>
${this._place ? html`<div class="place-hint">${t("remind_on_site_hint", L)}</div>` : nothing}
`;
}
private async _save(): Promise<void> {
if (this._loading) return; // synchronous re-entry guard (double-click)
if (!this._name.trim()) return;
@@ -211,6 +249,8 @@ export class MaintenanceObjectDialog extends LitElement {
notes: this._notes.trim() || null,
ha_device_id: this._haDeviceId || null,
parent_entry_id: this._parentEntryId || null,
// Only with the feature on: switched off, a stored place is kept as is.
...(this.placesEnabled ? { place: this._place || null, remind_on_site: !!this._place && this._remindOnSite } : {}),
};
const res = await runWs<{ device_swap?: DeviceSwap }>(
this,
@@ -278,6 +318,7 @@ export class MaintenanceObjectDialog extends LitElement {
@value-changed=${(e: CustomEvent) =>
(this._areaId = (e.detail.value as string) || "")}
></ha-area-picker>
${this.placesEnabled ? this._renderPlace(L) : nothing}
<ms-date-field
kind="date"
clearable
@@ -352,6 +393,25 @@ export class MaintenanceObjectDialog extends LitElement {
}
static styles = css`
.place-on-site {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: var(--primary-text-color);
}
.place-on-site.disabled {
color: var(--disabled-text-color, var(--secondary-text-color));
}
.place-hint,
.place-missing {
font-size: 12px;
color: var(--secondary-text-color);
margin-top: -4px;
}
.place-missing {
color: var(--warning-color, #c77700);
}
.dialog-title {
font-size: 18px;
font-weight: 500;
@@ -703,6 +703,7 @@ export class MaintenanceSettingsView extends LitElement {
{ key: "checklists", settingKey: "advanced_checklists_visible", label: t("feat_checklists", L), desc: t("feat_checklists_desc", L) },
{ key: "schedule_time", settingKey: "advanced_schedule_time_visible", label: t("feat_schedule_time", L), desc: t("feat_schedule_time_desc", L) },
{ key: "completion_actions", settingKey: "advanced_completion_actions_visible", label: t("feat_completion_actions", L), desc: t("feat_completion_actions_desc", L) },
{ key: "places", settingKey: "advanced_places_visible", label: t("feat_places", L), desc: t("feat_places_desc", L) },
];
return html`
@@ -28,6 +28,7 @@ import type { TriggerType } from "../types";
import { REQUIRED_COMPLETION_KEYS, REQUIRED_COMPLETION_LABELS } from "./required-completion-labels";
import "./ms-textfield";
import { defaultNotifyIcon } from "../helpers/notify-icons";
import { primaryTriggerEntity, triggerEntityIds } from "../helpers/trigger-entities";
import "./ms-date-field";
import "./ms-cost-input";
@@ -160,7 +161,7 @@ export function applyTypeFields(c: TriggerConfig, d: TriggerTypeFields): void {
/** Map a persisted compound condition (storage shape) to an editable draft. */
function conditionToDraft(c: TriggerConfig): CompoundConditionDraft {
const ids = c.entity_ids || (c.entity_id ? [c.entity_id] : []);
const ids = triggerEntityIds(c);
return {
entityIds: ids.join(", "),
...typeFieldsFromConfig(c),
@@ -617,8 +618,8 @@ export class MaintenanceTaskDialog extends LitElement {
// Fleet task) must still hydrate the singular field — the save path
// gates on _triggerEntityId and would otherwise NULL the whole trigger
// on an unrelated edit (issue #106).
this._triggerEntityId = tc.entity_id || (tc.entity_ids && tc.entity_ids[0]) || "";
this._triggerEntityIds = tc.entity_ids || (tc.entity_id ? [tc.entity_id] : []);
this._triggerEntityId = primaryTriggerEntity(tc) || "";
this._triggerEntityIds = triggerEntityIds(tc);
this._triggerEntityLogic = tc.entity_logic || "any";
this._setTypeFields(typeFieldsFromConfig(tc));
this._triggerCombinator = tc.trigger_combinator === "all" ? "all" : "any";
@@ -78,7 +78,7 @@ export class MaintenanceTaskQuickActionsDialog extends LitElement {
@state() private _features: AdvancedFeatures = {
adaptive: false, seasonal: false, environmental: false,
budget: false, groups: false, checklists: false, schedule_time: false,
completion_actions: false,
completion_actions: false, places: false,
};
@state() private _toast = "";
private readonly _toastTimer = new ToastTimer();
@@ -129,9 +129,19 @@ export function openCreateObjectDialog(): boolean {
if (!syncHass(dlg)) return false;
// openCreate is defined on MaintenanceObjectDialog
dlg.openCreate();
void applyObjectDialogSettings(dlg);
return true;
}
/** 2026-10 places: the object dialog shows its place fields by the same
* feature switch as in the panel. Opened at once; the fields appear when
* the (page-wide cached) settings are in. */
async function applyObjectDialogSettings(dlg: MaintenanceObjectDialog): Promise<void> {
const hass = getHass();
if (!hass) return;
dlg.placesEnabled = (await fetchSettingsOnce(hass)).features.places;
}
export function openEditObjectDialog(
entryId: string,
obj: MaintenanceObject,
@@ -139,6 +149,7 @@ export function openEditObjectDialog(
const dlg = getOrCreate<MaintenanceObjectDialog>(OBJECT_DIALOG_TAG);
if (!syncHass(dlg)) return false;
dlg.openEdit(entryId, obj);
void applyObjectDialogSettings(dlg);
return true;
}
@@ -545,6 +545,11 @@
"no_objects": "Zatím žádné objekty.",
"action_error": "Akce se nezdařila. Zkuste to znovu.",
"area_id_optional": "Oblast (volitelná)",
"place_optional": "Místo (zóna, volitelné)",
"remind_on_site": "Připomínat jen, když tam někdo je",
"remind_on_site_hint": "Připomínky čekají, dokud někdo nebude v této zóně. Kdo tam přijede, dostane zprávu s tím, co je potřeba udělat.",
"place_missing": "Zóna nenalezena: připomínky jako doma",
"place_home": "Domov",
"installation_date_optional": "Datum instalace (volitelné)",
"warranty_expiry_optional": "Konec záruky (volitelné)",
"warranty": "Záruka",
@@ -781,6 +786,8 @@
"owner_label": "Vlastník",
"feat_completion_actions": "Akce při dokončení",
"feat_completion_actions_desc": "Akce HA na úlohu při dokončení + QR rychlého dokončení s předvolenými hodnotami.",
"feat_places": "Místa",
"feat_places_desc": "Objekty udržované na jiném místě (zóna Home Assistantu): připomínky jen když tam někdo je a zpráva při příjezdu.",
"on_complete_action_title": "Při dokončení: spustit akci HA (volitelné)",
"on_complete_action_desc": "Volá službu HA, když je úloha dokončena — např. resetuje čítač na zařízení.",
"on_complete_action_target": "Cílová entita",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Používá díl: {name}",
"label_filter": "Štítek",
"all_labels": "Všechny štítky",
"filter_place": "Místo",
"filter_place_all": "Všechna místa",
"settings_notify_scope": "Upozorňovat jen pro zobrazení",
"settings_notify_scope_all": "Všechny úkoly",
"settings_notify_scope_hint": "Připomenutí posílají jen úkoly odpovídající filtrům štítku/uživatele zvoleného zobrazení. Stav, řazení a seskupení zobrazení se zde ignorují.",
@@ -547,6 +547,11 @@
"no_objects": "Ingen objekter endnu.",
"action_error": "Handlingen mislykkedes. Prøv igen.",
"area_id_optional": "Område (valgfrit)",
"place_optional": "Sted (zone, valgfrit)",
"remind_on_site": "Påmind kun, når nogen er der",
"remind_on_site_hint": "Påmindelser venter, til nogen er i denne zone. Den, der ankommer, får en besked med det, der skal gøres.",
"place_missing": "Zone ikke fundet: påmindelser som hjemme",
"place_home": "Hjemme",
"installation_date_optional": "Installationsdato (valgfrit)",
"warranty_expiry_optional": "Garantiens udløb (valgfrit)",
"warranty": "Garanti",
@@ -806,6 +811,8 @@
"owner_label": "Ejer",
"feat_completion_actions": "Fuldførelseshandlinger",
"feat_completion_actions_desc": "HA-handling pr. opgave ved fuldførelse + hurtig-fuldfør-QR med forudindstillede værdier.",
"feat_places": "Steder",
"feat_places_desc": "Objekter, der vedligeholdes et andet sted (en Home Assistant-zone): påmindelser kun når nogen er der, og en besked ved ankomst.",
"on_complete_action_title": "Ved fuldførelse: udløs HA-handling (valgfrit)",
"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",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Bruger reservedel: {name}",
"label_filter": "Etiket",
"all_labels": "Alle etiketter",
"filter_place": "Sted",
"filter_place_all": "Alle steder",
"settings_notify_scope": "Underret kun for visning",
"settings_notify_scope_all": "Alle opgaver",
"settings_notify_scope_hint": "Kun opgaver, der matcher den valgte visnings etiket-/brugerfiltre, sender påmindelser. Visningens status, sortering og gruppering ignoreres her.",
@@ -547,6 +547,11 @@
"no_objects": "Keine Objekte vorhanden.",
"action_error": "Aktion fehlgeschlagen. Bitte erneut versuchen.",
"area_id_optional": "Bereich (optional)",
"place_optional": "Ort (Zone, optional)",
"remind_on_site": "Nur erinnern, wenn jemand dort ist",
"remind_on_site_hint": "Erinnerungen warten, bis jemand in dieser Zone ist. Wer dort ankommt, bekommt eine Nachricht mit dem, was fällig ist.",
"place_missing": "Zone nicht gefunden: Erinnerungen wie zuhause",
"place_home": "Zuhause",
"installation_date_optional": "Installationsdatum (optional)",
"warranty_expiry_optional": "Garantie-Ablauf (optional)",
"warranty": "Garantie",
@@ -806,6 +811,8 @@
"owner_label": "Besitzer",
"feat_completion_actions": "Completion-Actions",
"feat_completion_actions_desc": "Pro Aufgabe HA-Action beim Abschluss konfigurieren + Quick-Complete-QR mit voreingestellten Werten.",
"feat_places": "Orte",
"feat_places_desc": "Objekte an einem anderen Ort (eine Home-Assistant-Zone): Erinnerungen nur, wenn jemand dort ist, und eine Nachricht bei Ankunft.",
"on_complete_action_title": "Beim Abschluss: HA-Action auslösen (optional)",
"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",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Nutzt Ersatzteil: {name}",
"label_filter": "Label",
"all_labels": "Alle Labels",
"filter_place": "Ort",
"filter_place_all": "Alle Orte",
"settings_notify_scope": "Nur benachrichtigen für Ansicht",
"settings_notify_scope_all": "Alle Aufgaben",
"settings_notify_scope_hint": "Nur Aufgaben, die zu den Label-/Nutzer-Filtern der gewählten Ansicht passen, senden Erinnerungen. Status, Sortierung und Gruppierung der Ansicht werden hier ignoriert.",
@@ -547,6 +547,11 @@
"no_objects": "No objects yet.",
"action_error": "Action failed. Please try again.",
"area_id_optional": "Area (optional)",
"place_optional": "Place (zone, optional)",
"remind_on_site": "Remind only while somebody is there",
"remind_on_site_hint": "Reminders wait until somebody is in this zone. Whoever arrives there gets a message with what is due.",
"place_missing": "Zone not found: reminders as at home",
"place_home": "Home",
"installation_date_optional": "Installation date (optional)",
"warranty_expiry_optional": "Warranty expiry (optional)",
"warranty": "Warranty",
@@ -813,6 +818,8 @@
"owner_label": "Owner",
"feat_completion_actions": "Completion actions",
"feat_completion_actions_desc": "Per-task HA action on complete + quick-complete QR with pre-set values.",
"feat_places": "Places",
"feat_places_desc": "Objects maintained at another place (a Home Assistant zone): reminders only while somebody is there, and a message on arrival.",
"on_complete_action_title": "On complete: trigger HA action (optional)",
"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",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Uses part: {name}",
"label_filter": "Label",
"all_labels": "All labels",
"filter_place": "Place",
"filter_place_all": "All places",
"settings_notify_scope": "Notify only for view",
"settings_notify_scope_all": "All tasks",
"settings_notify_scope_hint": "Only tasks matching the selected saved view's label/user filters send reminders. Status, sorting and grouping of the view are ignored here.",
@@ -545,6 +545,11 @@
"no_objects": "Aún no hay objetos.",
"action_error": "Acción fallida. Inténtelo de nuevo.",
"area_id_optional": "Área (opcional)",
"place_optional": "Lugar (zona, opcional)",
"remind_on_site": "Recordar solo cuando alguien esté allí",
"remind_on_site_hint": "Los recordatorios esperan a que alguien esté en esta zona. Quien llega allí recibe un mensaje con lo pendiente.",
"place_missing": "Zona no encontrada: recordatorios como en casa",
"place_home": "Casa",
"installation_date_optional": "Fecha de instalación (opcional)",
"warranty_expiry_optional": "Vencimiento de garantía (opcional)",
"warranty": "Garantía",
@@ -781,6 +786,8 @@
"owner_label": "Propietario",
"feat_completion_actions": "Acciones de finalización",
"feat_completion_actions_desc": "Acción HA por tarea al completar + QR de finalización rápida con valores prefijados.",
"feat_places": "Lugares",
"feat_places_desc": "Objetos mantenidos en otro lugar (una zona de Home Assistant): recordatorios solo cuando alguien está allí y un mensaje al llegar.",
"on_complete_action_title": "Al completar: ejecutar acción HA (opcional)",
"on_complete_action_desc": "Llama un servicio HA cuando se completa la tarea — p. ej. reiniciar un contador del dispositivo.",
"on_complete_action_target": "Entidad objetivo",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Usa la pieza: {name}",
"label_filter": "Etiqueta",
"all_labels": "Todas las etiquetas",
"filter_place": "Lugar",
"filter_place_all": "Todos los lugares",
"settings_notify_scope": "Notificar solo para la vista",
"settings_notify_scope_all": "Todas las tareas",
"settings_notify_scope_hint": "Solo las tareas que coinciden con los filtros de etiqueta/usuario de la vista seleccionada envían recordatorios. El estado, el orden y la agrupación de la vista se ignoran aquí.",
@@ -547,6 +547,11 @@
"no_objects": "Ei vielä kohteita.",
"action_error": "Toiminto epäonnistui. Yritä uudelleen.",
"area_id_optional": "Alue (valinnainen)",
"place_optional": "Paikka (vyöhyke, valinnainen)",
"remind_on_site": "Muistuta vain, kun joku on paikalla",
"remind_on_site_hint": "Muistutukset odottavat, kunnes joku on tällä vyöhykkeellä. Sinne saapuva saa viestin tehtävistä asioista.",
"place_missing": "Vyöhykettä ei löydy: muistutukset kuin kotona",
"place_home": "Koti",
"installation_date_optional": "Asennuspäivä (valinnainen)",
"warranty_expiry_optional": "Takuun päättyminen (valinnainen)",
"warranty": "Takuu",
@@ -806,6 +811,8 @@
"owner_label": "Omistaja",
"feat_completion_actions": "Suoritustoiminnot",
"feat_completion_actions_desc": "Tehtäväkohtainen HA-toiminto suorituksen yhteydessä + pikasuoritus-QR esiasetetuilla arvoilla.",
"feat_places": "Paikat",
"feat_places_desc": "Toisessa paikassa (Home Assistant -vyöhyke) huollettavat kohteet: muistutukset vain, kun joku on paikalla, ja viesti saavuttaessa.",
"on_complete_action_title": "Suorituksen yhteydessä: laukaise HA-toiminto (valinnainen)",
"on_complete_action_desc": "Kutsuu HA-palvelua, kun tehtävä suoritetaan — esim. nollaa laitteen laskuri.",
"on_complete_action_target": "Kohde-entiteetti",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Käyttää varaosaa: {name}",
"label_filter": "Tunniste",
"all_labels": "Kaikki tunnisteet",
"filter_place": "Paikka",
"filter_place_all": "Kaikki paikat",
"settings_notify_scope": "Ilmoita vain näkymälle",
"settings_notify_scope_all": "Kaikki tehtävät",
"settings_notify_scope_hint": "Vain valitun näkymän tunniste-/käyttäjäsuodattimiin osuvat tehtävät lähettävät muistutuksia. Näkymän tila, lajittelu ja ryhmittely ohitetaan tässä.",
@@ -545,6 +545,11 @@
"no_objects": "Aucun objet pour l'instant.",
"action_error": "Action échouée. Veuillez réessayer.",
"area_id_optional": "Zone (optionnel)",
"place_optional": "Lieu (zone, facultatif)",
"remind_on_site": "Rappeler seulement quand quelqu'un y est",
"remind_on_site_hint": "Les rappels attendent que quelqu'un soit dans cette zone. Qui y arrive reçoit un message avec ce qui est à faire.",
"place_missing": "Zone introuvable : rappels comme à la maison",
"place_home": "Domicile",
"installation_date_optional": "Date d'installation (optionnel)",
"warranty_expiry_optional": "Fin de garantie (optionnel)",
"warranty": "Garantie",
@@ -781,6 +786,8 @@
"owner_label": "Propriétaire",
"feat_completion_actions": "Actions de finalisation",
"feat_completion_actions_desc": "Action HA par tâche lors de la finalisation + QR de finalisation rapide avec valeurs pré-définies.",
"feat_places": "Lieux",
"feat_places_desc": "Objets entretenus à un autre endroit (une zone Home Assistant) : rappels seulement quand quelqu'un y est, et un message à l'arrivée.",
"on_complete_action_title": "À la finalisation : déclencher une action HA (optionnel)",
"on_complete_action_desc": "Appelle un service HA quand la tâche est terminée — p. ex. réinitialiser un compteur sur l'appareil.",
"on_complete_action_target": "Entité cible",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Utilise la pièce : {name}",
"label_filter": "Étiquette",
"all_labels": "Toutes les étiquettes",
"filter_place": "Lieu",
"filter_place_all": "Tous les lieux",
"settings_notify_scope": "Notifier uniquement pour la vue",
"settings_notify_scope_all": "Toutes les tâches",
"settings_notify_scope_hint": "Seules les tâches correspondant aux filtres étiquette/utilisateur de la vue choisie envoient des rappels. Le statut, le tri et le regroupement de la vue sont ignorés ici.",
@@ -547,6 +547,11 @@
"no_objects": "अभी कोई वस्तु नहीं।",
"action_error": "क्रिया विफल। कृपया पुनः प्रयास करें।",
"area_id_optional": "क्षेत्र (वैकल्पिक)",
"place_optional": "स्थान (ज़ोन, वैकल्पिक)",
"remind_on_site": "केवल तब याद दिलाएँ जब कोई वहाँ हो",
"remind_on_site_hint": "रिमाइंडर तब तक रुकते हैं जब तक कोई इस ज़ोन में न हो। वहाँ पहुँचने वाले को बाकी कामों का संदेश मिलता है।",
"place_missing": "ज़ोन नहीं मिला: रिमाइंडर घर जैसे",
"place_home": "घर",
"installation_date_optional": "स्थापना तिथि (वैकल्पिक)",
"warranty_expiry_optional": "वारंटी समाप्ति (वैकल्पिक)",
"warranty": "वारंटी",
@@ -806,6 +811,8 @@
"owner_label": "स्वामी",
"feat_completion_actions": "पूर्णता क्रियाएँ",
"feat_completion_actions_desc": "पूर्ण होने पर प्रति-कार्य HA क्रिया + पूर्व-निर्धारित मानों के साथ त्वरित-पूर्ण QR।",
"feat_places": "स्थान",
"feat_places_desc": "किसी दूसरी जगह (Home Assistant ज़ोन) पर रखरखाव वाले ऑब्जेक्ट: रिमाइंडर तभी जब कोई वहाँ हो, और पहुँचने पर एक संदेश।",
"on_complete_action_title": "पूर्ण होने पर: HA क्रिया ट्रिगर करें (वैकल्पिक)",
"on_complete_action_desc": "कार्य पूर्ण होने पर एक HA सेवा कॉल करता है — उदा. डिवाइस पर काउंटर रीसेट करना।",
"on_complete_action_target": "लक्ष्य एंटिटी",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "पुर्ज़ा उपयोग करता है: {name}",
"label_filter": "लेबल",
"all_labels": "सभी लेबल",
"filter_place": "स्थान",
"filter_place_all": "सभी स्थान",
"settings_notify_scope": "केवल इस व्यू के लिए सूचित करें",
"settings_notify_scope_all": "सभी कार्य",
"settings_notify_scope_hint": "केवल वे कार्य अनुस्मारक भेजते हैं जो चुने गए व्यू के लेबल/उपयोगकर्ता फ़िल्टर से मेल खाते हैं। व्यू की स्थिति, क्रम और समूहन यहाँ अनदेखे रहते हैं।",
@@ -547,6 +547,11 @@
"no_objects": "Még nincsenek objektumok.",
"action_error": "A művelet nem sikerült. Próbálja újra.",
"area_id_optional": "Terület (opcionális)",
"place_optional": "Helyszín (zóna, opcionális)",
"remind_on_site": "Csak akkor emlékeztessen, ha valaki ott van",
"remind_on_site_hint": "Az emlékeztetők megvárják, amíg valaki ebben a zónában lesz. Aki odaér, üzenetet kap az esedékes teendőkről.",
"place_missing": "A zóna nem található: emlékeztetők, mint otthon",
"place_home": "Otthon",
"installation_date_optional": "Telepítés dátuma (opcionális)",
"warranty_expiry_optional": "Garancia lejárta (opcionális)",
"warranty": "Garancia",
@@ -813,6 +818,8 @@
"owner_label": "Tulajdonos",
"feat_completion_actions": "Elvégzési műveletek",
"feat_completion_actions_desc": "Feladatonkénti HA művelet elvégzéskor + gyorselvégző QR előre beállított értékekkel.",
"feat_places": "Helyszínek",
"feat_places_desc": "Másik helyszínen (Home Assistant-zóna) karbantartott objektumok: emlékeztető csak akkor, ha valaki ott van, és üzenet érkezéskor.",
"on_complete_action_title": "Elvégzéskor: HA művelet indítása (opcionális)",
"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",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Alkatrészt használ: {name}",
"label_filter": "Címke",
"all_labels": "Minden címke",
"filter_place": "Helyszín",
"filter_place_all": "Minden helyszín",
"settings_notify_scope": "Értesítés csak erre a nézetre",
"settings_notify_scope_all": "Minden feladat",
"settings_notify_scope_hint": "Csak a kiválasztott mentett nézet címke-/felhasználószűrőinek megfelelő feladatok küldenek emlékeztetőt. A nézet állapot-, rendezési és csoportosítási beállításait itt figyelmen kívül hagyjuk.",
@@ -545,6 +545,11 @@
"no_objects": "Nessun oggetto ancora.",
"action_error": "Azione fallita. Riprova.",
"area_id_optional": "Area (opzionale)",
"place_optional": "Luogo (zona, facoltativo)",
"remind_on_site": "Ricorda solo quando qualcuno è lì",
"remind_on_site_hint": "I promemoria aspettano che qualcuno sia in questa zona. Chi arriva lì riceve un messaggio con ciò che è da fare.",
"place_missing": "Zona non trovata: promemoria come a casa",
"place_home": "Casa",
"installation_date_optional": "Data di installazione (opzionale)",
"warranty_expiry_optional": "Scadenza garanzia (opzionale)",
"warranty": "Garanzia",
@@ -781,6 +786,8 @@
"owner_label": "Proprietario",
"feat_completion_actions": "Azioni di completamento",
"feat_completion_actions_desc": "Azione HA per attività al completamento + QR completamento rapido con valori predefiniti.",
"feat_places": "Luoghi",
"feat_places_desc": "Oggetti curati in un altro luogo (una zona di Home Assistant): promemoria solo quando qualcuno è lì e un messaggio all'arrivo.",
"on_complete_action_title": "Al completamento: attiva azione HA (opzionale)",
"on_complete_action_desc": "Chiama un servizio HA quando l'attività viene completata — es. azzerare un contatore sul dispositivo.",
"on_complete_action_target": "Entità target",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Usa il ricambio: {name}",
"label_filter": "Etichetta",
"all_labels": "Tutte le etichette",
"filter_place": "Luogo",
"filter_place_all": "Tutti i luoghi",
"settings_notify_scope": "Notifica solo per la vista",
"settings_notify_scope_all": "Tutte le attività",
"settings_notify_scope_hint": "Solo le attività che corrispondono ai filtri etichetta/utente della vista scelta inviano promemoria. Stato, ordinamento e raggruppamento della vista qui vengono ignorati.",
@@ -547,6 +547,11 @@
"no_objects": "対象がまだありません。",
"action_error": "操作に失敗しました。もう一度お試しください。",
"area_id_optional": "エリア(任意)",
"place_optional": "場所(ゾーン、任意)",
"remind_on_site": "誰かがいるときだけリマインド",
"remind_on_site_hint": "リマインドは誰かがこのゾーンにいるまで待機します。到着した人には、やるべきことをまとめたメッセージが届きます。",
"place_missing": "ゾーンが見つかりません:自宅と同じようにリマインドします",
"place_home": "自宅",
"installation_date_optional": "設置日(任意)",
"warranty_expiry_optional": "保証期限(任意)",
"warranty": "保証",
@@ -806,6 +811,8 @@
"owner_label": "オーナー",
"feat_completion_actions": "完了時アクション",
"feat_completion_actions_desc": "タスクごとの完了時HAアクション+事前設定値付きのクイック完了QR。",
"feat_places": "場所",
"feat_places_desc": "別の場所(Home Assistant のゾーン)で管理するオブジェクト:誰かがその場にいるときだけリマインドし、到着時に通知します。",
"on_complete_action_title": "完了時: HAアクションを実行(任意)",
"on_complete_action_desc": "タスク完了時にHAサービスを呼び出します — 例: デバイスのカウンターをリセット。",
"on_complete_action_target": "対象エンティティ",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "使用する部品: {name}",
"label_filter": "ラベル",
"all_labels": "すべてのラベル",
"filter_place": "場所",
"filter_place_all": "すべての場所",
"settings_notify_scope": "このビューのみ通知",
"settings_notify_scope_all": "すべてのタスク",
"settings_notify_scope_hint": "選択した保存済みビューのラベル/ユーザーフィルターに一致するタスクのみがリマインダーを送信します。ビューのステータス・並び順・グループ化はここでは無視されます。",
@@ -547,6 +547,11 @@
"no_objects": "아직 객체가 없습니다.",
"action_error": "동작에 실패했습니다. 다시 시도해 주세요.",
"area_id_optional": "구역 (선택)",
"place_optional": "장소 (구역, 선택 사항)",
"remind_on_site": "누군가 있을 때만 알림",
"remind_on_site_hint": "누군가 이 구역에 있을 때까지 알림이 대기합니다. 그곳에 도착한 사람은 해야 할 일을 담은 메시지를 받습니다.",
"place_missing": "구역을 찾을 수 없음: 집과 같이 알림",
"place_home": "집",
"installation_date_optional": "설치일 (선택)",
"warranty_expiry_optional": "보증 만료일 (선택)",
"warranty": "보증",
@@ -813,6 +818,8 @@
"owner_label": "소유자",
"feat_completion_actions": "완료 동작",
"feat_completion_actions_desc": "완료 시 작업별 HA 동작 실행 + 사전 설정값이 있는 빠른 완료 QR.",
"feat_places": "장소",
"feat_places_desc": "다른 장소(Home Assistant 구역)에서 관리하는 개체: 누군가 그곳에 있을 때만 알림을 보내고, 도착하면 메시지를 보냅니다.",
"on_complete_action_title": "완료 시: HA 동작 실행 (선택)",
"on_complete_action_desc": "작업이 완료되면 HA 서비스를 호출합니다 — 예: 기기의 카운터 초기화.",
"on_complete_action_target": "대상 엔티티",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "사용 부품: {name}",
"label_filter": "라벨",
"all_labels": "모든 라벨",
"filter_place": "장소",
"filter_place_all": "모든 장소",
"settings_notify_scope": "특정 보기만 알림",
"settings_notify_scope_all": "모든 작업",
"settings_notify_scope_hint": "선택한 저장된 보기의 라벨/사용자 필터와 일치하는 작업만 알림을 보냅니다. 보기의 상태·정렬·그룹화는 여기서 무시됩니다.",
@@ -547,6 +547,11 @@
"no_objects": "Ingen objekter ennå.",
"action_error": "Handlingen mislyktes. Prøv igjen.",
"area_id_optional": "Område (valgfritt)",
"place_optional": "Sted (sone, valgfritt)",
"remind_on_site": "Påminn bare når noen er der",
"remind_on_site_hint": "Påminnelser venter til noen er i denne sonen. Den som kommer dit, får en melding med det som skal gjøres.",
"place_missing": "Fant ikke sonen: påminnelser som hjemme",
"place_home": "Hjemme",
"installation_date_optional": "Installasjonsdato (valgfritt)",
"warranty_expiry_optional": "Garanti utløper (valgfritt)",
"warranty": "Garanti",
@@ -806,6 +811,8 @@
"owner_label": "Eier",
"feat_completion_actions": "Fullføringshandlinger",
"feat_completion_actions_desc": "HA-handling per oppgave ved fullføring + hurtigfullføring med QR og forhåndsinnstilte verdier.",
"feat_places": "Steder",
"feat_places_desc": "Objekter som vedlikeholdes et annet sted (en Home Assistant-sone): påminnelser bare når noen er der, og en melding ved ankomst.",
"on_complete_action_title": "Ved fullføring: utløs HA-handling (valgfritt)",
"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",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Bruker del: {name}",
"label_filter": "Etikett",
"all_labels": "Alle etiketter",
"filter_place": "Sted",
"filter_place_all": "Alle steder",
"settings_notify_scope": "Varsle kun for visning",
"settings_notify_scope_all": "Alle oppgaver",
"settings_notify_scope_hint": "Bare oppgaver som matcher etikett-/brukerfiltrene til valgt visning sender påminnelser. Visningens status, sortering og gruppering ignoreres her.",
@@ -545,6 +545,11 @@
"no_objects": "Nog geen objecten.",
"action_error": "Actie mislukt. Probeer het opnieuw.",
"area_id_optional": "Gebied (optioneel)",
"place_optional": "Locatie (zone, optioneel)",
"remind_on_site": "Alleen herinneren als er iemand is",
"remind_on_site_hint": "Herinneringen wachten tot iemand in deze zone is. Wie daar aankomt, krijgt een bericht met wat er te doen is.",
"place_missing": "Zone niet gevonden: herinneringen zoals thuis",
"place_home": "Thuis",
"installation_date_optional": "Installatiedatum (optioneel)",
"warranty_expiry_optional": "Garantie verloopt (optioneel)",
"warranty": "Garantie",
@@ -781,6 +786,8 @@
"owner_label": "Eigenaar",
"feat_completion_actions": "Voltooiings-acties",
"feat_completion_actions_desc": "Per taak HA-actie bij voltooien + snel-voltooien-QR met vooraf ingestelde waarden.",
"feat_places": "Locaties",
"feat_places_desc": "Objecten op een andere locatie (een Home Assistant-zone): herinneringen alleen als er iemand is, en een bericht bij aankomst.",
"on_complete_action_title": "Bij voltooien: HA-actie uitvoeren (optioneel)",
"on_complete_action_desc": "Roept een HA-service aan wanneer de taak is voltooid — bv. een teller op het apparaat resetten.",
"on_complete_action_target": "Doel-entiteit",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Gebruikt onderdeel: {name}",
"label_filter": "Label",
"all_labels": "Alle labels",
"filter_place": "Locatie",
"filter_place_all": "Alle locaties",
"settings_notify_scope": "Alleen melden voor weergave",
"settings_notify_scope_all": "Alle taken",
"settings_notify_scope_hint": "Alleen taken die overeenkomen met de label-/gebruikersfilters van de gekozen weergave sturen herinneringen. Status, sortering en groepering van de weergave worden hier genegeerd.",
@@ -545,6 +545,11 @@
"no_objects": "Brak obiektów.",
"action_error": "Akcja nie powiodła się. Spróbuj ponownie.",
"area_id_optional": "Obszar (opcjonalny)",
"place_optional": "Miejsce (strefa, opcjonalnie)",
"remind_on_site": "Przypominaj tylko, gdy ktoś tam jest",
"remind_on_site_hint": "Przypomnienia czekają, aż ktoś będzie w tej strefie. Kto tam przyjedzie, dostaje wiadomość z tym, co jest do zrobienia.",
"place_missing": "Nie znaleziono strefy: przypomnienia jak w domu",
"place_home": "Dom",
"installation_date_optional": "Data instalacji (opcjonalna)",
"warranty_expiry_optional": "Wygaśnięcie gwarancji (opcjonalnie)",
"warranty": "Gwarancja",
@@ -781,6 +786,8 @@
"owner_label": "Właściciel",
"feat_completion_actions": "Akcje po zakończeniu",
"feat_completion_actions_desc": "Akcja HA per zadanie po zakończeniu + QR szybkiego zakończenia z predefiniowanymi wartościami.",
"feat_places": "Miejsca",
"feat_places_desc": "Obiekty w innym miejscu (strefa Home Assistant): przypomnienia tylko wtedy, gdy ktoś tam jest, i wiadomość po przybyciu.",
"on_complete_action_title": "Po zakończeniu: wywołaj akcję HA (opcjonalnie)",
"on_complete_action_desc": "Wywołuje usługę HA po zakończeniu zadania — np. zresetuj licznik urządzenia.",
"on_complete_action_target": "Encja docelowa",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Używa części: {name}",
"label_filter": "Etykieta",
"all_labels": "Wszystkie etykiety",
"filter_place": "Miejsce",
"filter_place_all": "Wszystkie miejsca",
"settings_notify_scope": "Powiadamiaj tylko dla widoku",
"settings_notify_scope_all": "Wszystkie zadania",
"settings_notify_scope_hint": "Przypomnienia wysyłają tylko zadania pasujące do filtrów etykiety/użytkownika wybranego widoku. Status, sortowanie i grupowanie widoku są tu ignorowane.",
@@ -547,6 +547,11 @@
"no_objects": "Ainda não há objetos.",
"action_error": "A ação falhou. Tente novamente.",
"area_id_optional": "Área (opcional)",
"place_optional": "Local (zona, opcional)",
"remind_on_site": "Lembrar só quando alguém estiver lá",
"remind_on_site_hint": "Os lembretes esperam até alguém estar nesta zona. Quem chega lá recebe uma mensagem com o que está pendente.",
"place_missing": "Zona não encontrada: lembretes como em casa",
"place_home": "Casa",
"installation_date_optional": "Data de instalação (opcional)",
"warranty_expiry_optional": "Fim da garantia (opcional)",
"warranty": "Garantia",
@@ -813,6 +818,8 @@
"owner_label": "Proprietário",
"feat_completion_actions": "Ações de conclusão",
"feat_completion_actions_desc": "Ação do HA por tarefa ao concluir + QR de conclusão rápida com valores predefinidos.",
"feat_places": "Locais",
"feat_places_desc": "Objetos mantidos em outro local (uma zona do Home Assistant): lembretes só quando alguém está lá e uma mensagem na chegada.",
"on_complete_action_title": "Ao concluir: acionar ação do HA (opcional)",
"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",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Usa a peça: {name}",
"label_filter": "Etiqueta",
"all_labels": "Todas as etiquetas",
"filter_place": "Local",
"filter_place_all": "Todos os locais",
"settings_notify_scope": "Notificar apenas para a visualização",
"settings_notify_scope_all": "Todas as tarefas",
"settings_notify_scope_hint": "Apenas tarefas que correspondem aos filtros de etiqueta/usuário da visualização salva selecionada enviam lembretes. Status, ordenação e agrupamento da visualização são ignorados aqui.",
@@ -545,6 +545,11 @@
"no_objects": "Ainda sem objetos.",
"action_error": "Ação falhada. Tente novamente.",
"area_id_optional": "Área (opcional)",
"place_optional": "Local (zona, opcional)",
"remind_on_site": "Lembrar só quando alguém lá estiver",
"remind_on_site_hint": "Os lembretes esperam até alguém estar nesta zona. Quem lá chega recebe uma mensagem com o que está pendente.",
"place_missing": "Zona não encontrada: lembretes como em casa",
"place_home": "Casa",
"installation_date_optional": "Data de instalação (opcional)",
"warranty_expiry_optional": "Validade da garantia (opcional)",
"warranty": "Garantia",
@@ -781,6 +786,8 @@
"owner_label": "Proprietário",
"feat_completion_actions": "Ações de conclusão",
"feat_completion_actions_desc": "Ação HA por tarefa ao concluir + QR de conclusão rápida com valores predefinidos.",
"feat_places": "Locais",
"feat_places_desc": "Objetos mantidos noutro local (uma zona do Home Assistant): lembretes só quando alguém lá está e uma mensagem à chegada.",
"on_complete_action_title": "Na conclusão: acionar ação HA (opcional)",
"on_complete_action_desc": "Chama um serviço HA quando a tarefa é concluída — p. ex. reiniciar um contador no dispositivo.",
"on_complete_action_target": "Entidade alvo",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Usa a peça: {name}",
"label_filter": "Etiqueta",
"all_labels": "Todas as etiquetas",
"filter_place": "Local",
"filter_place_all": "Todos os locais",
"settings_notify_scope": "Notificar apenas para a vista",
"settings_notify_scope_all": "Todas as tarefas",
"settings_notify_scope_hint": "Apenas as tarefas que correspondem aos filtros de etiqueta/utilizador da vista escolhida enviam lembretes. O estado, a ordenação e o agrupamento da vista são ignorados aqui.",
@@ -545,6 +545,11 @@
"no_objects": "Пока нет объектов.",
"action_error": "Не удалось выполнить действие. Попробуйте ещё раз.",
"area_id_optional": "Зона (опционально)",
"place_optional": "Место (зона, необязательно)",
"remind_on_site": "Напоминать, только когда там кто-то есть",
"remind_on_site_hint": "Напоминания ждут, пока кто-нибудь окажется в этой зоне. Кто туда приезжает, получает сообщение о том, что нужно сделать.",
"place_missing": "Зона не найдена: напоминания как дома",
"place_home": "Дом",
"installation_date_optional": "Дата установки (опционально)",
"warranty_expiry_optional": "Окончание гарантии (опционально)",
"warranty": "Гарантия",
@@ -781,6 +786,8 @@
"owner_label": "Владелец",
"feat_completion_actions": "Действия при завершении",
"feat_completion_actions_desc": "HA-действие по задаче при завершении + QR быстрого завершения с предустановленными значениями.",
"feat_places": "Места",
"feat_places_desc": "Объекты в другом месте (зона Home Assistant): напоминания только когда там кто-то есть, и сообщение по прибытии.",
"on_complete_action_title": "При завершении: запустить HA-действие (опционально)",
"on_complete_action_desc": "Вызывает HA-сервис при завершении задачи — напр., сбросить счётчик на устройстве.",
"on_complete_action_target": "Целевая сущность",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Использует деталь: {name}",
"label_filter": "Метка",
"all_labels": "Все метки",
"filter_place": "Место",
"filter_place_all": "Все места",
"settings_notify_scope": "Уведомлять только для представления",
"settings_notify_scope_all": "Все задачи",
"settings_notify_scope_hint": "Напоминания отправляют только задачи, совпадающие с фильтрами метки/пользователя выбранного представления. Статус, сортировка и группировка представления здесь игнорируются.",
@@ -545,6 +545,11 @@
"no_objects": "Inga objekt än.",
"action_error": "Åtgärden misslyckades. Försök igen.",
"area_id_optional": "Område (valfritt)",
"place_optional": "Plats (zon, valfritt)",
"remind_on_site": "Påminn bara när någon är där",
"remind_on_site_hint": "Påminnelser väntar tills någon är i den här zonen. Den som kommer dit får ett meddelande med det som ska göras.",
"place_missing": "Zonen hittades inte: påminnelser som hemma",
"place_home": "Hemma",
"installation_date_optional": "Installationsdatum (valfritt)",
"warranty_expiry_optional": "Garantins utgång (valfritt)",
"warranty": "Garanti",
@@ -781,6 +786,8 @@
"owner_label": "Ägare",
"feat_completion_actions": "Slutförande-åtgärder",
"feat_completion_actions_desc": "HA-åtgärd per uppgift vid slutförande + snabb-slutför-QR med förinställda värden.",
"feat_places": "Platser",
"feat_places_desc": "Objekt som sköts på en annan plats (en Home Assistant-zon): påminnelser bara när någon är där, och ett meddelande vid ankomst.",
"on_complete_action_title": "Vid slutförande: utlös HA-åtgärd (valfritt)",
"on_complete_action_desc": "Anropar en HA-tjänst när uppgiften slutförs — t.ex. återställ en räknare på enheten.",
"on_complete_action_target": "Målentitet",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Använder reservdel: {name}",
"label_filter": "Etikett",
"all_labels": "Alla etiketter",
"filter_place": "Plats",
"filter_place_all": "Alla platser",
"settings_notify_scope": "Avisera endast för vy",
"settings_notify_scope_all": "Alla uppgifter",
"settings_notify_scope_hint": "Endast uppgifter som matchar den valda vyns etikett-/användarfilter skickar påminnelser. Vyns status, sortering och gruppering ignoreras här.",
@@ -547,6 +547,11 @@
"no_objects": "Henüz nesne yok.",
"action_error": "Eylem başarısız oldu. Lütfen tekrar deneyin.",
"area_id_optional": "Alan (isteğe bağlı)",
"place_optional": "Yer (bölge, isteğe bağlı)",
"remind_on_site": "Yalnızca biri oradayken hatırlat",
"remind_on_site_hint": "Hatırlatmalar biri bu bölgede olana kadar bekler. Oraya varan kişi yapılacakların listesini içeren bir mesaj alır.",
"place_missing": "Bölge bulunamadı: hatırlatmalar evdeki gibi",
"place_home": "Ev",
"installation_date_optional": "Kurulum tarihi (isteğe bağlı)",
"warranty_expiry_optional": "Garanti bitişi (isteğe bağlı)",
"warranty": "Garanti",
@@ -813,6 +818,8 @@
"owner_label": "Sahip",
"feat_completion_actions": "Tamamlama eylemleri",
"feat_completion_actions_desc": "Görev başına tamamlamada HA eylemi + önceden ayarlı değerlerle hızlı tamamlama QR'ı.",
"feat_places": "Yerler",
"feat_places_desc": "Başka bir yerde (Home Assistant bölgesi) bakımı yapılan nesneler: hatırlatmalar yalnızca biri oradayken, varışta bir mesaj.",
"on_complete_action_title": "Tamamlandığında: HA eylemi tetikle (isteğe bağlı)",
"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",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Kullanılan parça: {name}",
"label_filter": "Etiket",
"all_labels": "Tüm etiketler",
"filter_place": "Yer",
"filter_place_all": "Tüm yerler",
"settings_notify_scope": "Yalnızca şu görünüm için bildir",
"settings_notify_scope_all": "Tüm görevler",
"settings_notify_scope_hint": "Yalnızca seçili kayıtlı görünümün etiket/kullanıcı filtreleriyle eşleşen görevler hatırlatma gönderir. Görünümün durum, sıralama ve gruplaması burada dikkate alınmaz.",
@@ -545,6 +545,11 @@
"no_objects": "Поки немає об'єктів.",
"action_error": "Дія не вдалась. Спробуйте ще раз.",
"area_id_optional": "Зона (необов'язково)",
"place_optional": "Місце (зона, необов'язково)",
"remind_on_site": "Нагадувати, лише коли там хтось є",
"remind_on_site_hint": "Нагадування чекають, поки хтось буде в цій зоні. Хто туди прибуває, отримує повідомлення про те, що потрібно зробити.",
"place_missing": "Зону не знайдено: нагадування як удома",
"place_home": "Дім",
"installation_date_optional": "Дата встановлення (необов'язково)",
"warranty_expiry_optional": "Закінчення гарантії (необов'язково)",
"warranty": "Гарантія",
@@ -781,6 +786,8 @@
"owner_label": "Власник",
"feat_completion_actions": "Дії при завершенні",
"feat_completion_actions_desc": "Дія HA по завданню при завершенні + QR швидкого завершення з попередньо встановленими значеннями.",
"feat_places": "Місця",
"feat_places_desc": "Об'єкти в іншому місці (зона Home Assistant): нагадування лише тоді, коли там хтось є, і повідомлення після прибуття.",
"on_complete_action_title": "При завершенні: викликати HA-дію (опційно)",
"on_complete_action_desc": "Викликає HA-сервіс, коли завдання завершено — напр., скинути лічильник на пристрої.",
"on_complete_action_target": "Цільова сутність",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "Використовує деталь: {name}",
"label_filter": "Мітка",
"all_labels": "Усі мітки",
"filter_place": "Місце",
"filter_place_all": "Усі місця",
"settings_notify_scope": "Сповіщати лише для подання",
"settings_notify_scope_all": "Усі завдання",
"settings_notify_scope_hint": "Нагадування надсилають лише завдання, що відповідають фільтрам мітки/користувача обраного подання. Статус, сортування та групування подання тут ігноруються.",
@@ -547,6 +547,11 @@
"no_objects": "暂无维护项。",
"action_error": "操作失败。请重试。",
"area_id_optional": "区域 (可选)",
"place_optional": "地点(区域,可选)",
"remind_on_site": "仅当有人在场时提醒",
"remind_on_site_hint": "提醒会等到有人在此区域时再发送。到达那里的人会收到一条列出待办事项的消息。",
"place_missing": "未找到区域:按在家处理提醒",
"place_home": "家",
"installation_date_optional": "安装日期 (可选)",
"warranty_expiry_optional": "保修到期 (可选)",
"warranty": "保修",
@@ -806,6 +811,8 @@
"owner_label": "所有者",
"feat_completion_actions": "完成动作",
"feat_completion_actions_desc": "为每个任务配置完成时的 HA 动作 + 预设值的快速完成二维码。",
"feat_places": "地点",
"feat_places_desc": "在其他地点(Home Assistant 区域)维护的对象:仅当有人在场时提醒,并在到达时发送消息。",
"on_complete_action_title": "完成时:触发 HA 动作(可选)",
"on_complete_action_desc": "任务完成时调用 HA 服务 — 例如重置设备上的物理计数器。",
"on_complete_action_target": "目标实体",
@@ -1125,6 +1132,8 @@
"adopt_problem_part": "使用配件:{name}",
"label_filter": "标签",
"all_labels": "所有标签",
"filter_place": "地点",
"filter_place_all": "所有地点",
"settings_notify_scope": "仅为该视图发送通知",
"settings_notify_scope_all": "所有任务",
"settings_notify_scope_hint": "只有匹配所选视图的标签/用户筛选的任务才会发送提醒。视图的状态、排序和分组在此处被忽略。",
@@ -131,6 +131,7 @@ import type { PartsCostMode } from "./helpers/parts-cost";
import { canWrite } from "./helpers/permissions";
import { renderStatusBadge } from "./renderers/status";
import { TOAST_MS, ACTION_TOAST_MS } from "./helpers/toast";
import { primaryTriggerEntity } from "./helpers/trigger-entities";
import { buildHistoryEntryDraft } from "./helpers/history-draft";
import { readingSlotDelta } from "./helpers/reading-slots";
import { bulkResultMessage, codedFailures, runWs, runWsEach } from "./helpers/ws-run";
@@ -236,6 +237,8 @@ export class MaintenanceSupporterPanel extends LitElement {
@state() private _filterStatus = "";
@state() private _filterUser: string | null = null;
@state() private _filterLabel: string | null = null;
// 2026-10 places: "" = all, "home" = objects without a place, else a zone id.
@state() private _filterPlace = "";
/** #134: only tasks of this priority (low|normal|high); "" = no filter. */
@state() private _filterPriority = "";
// v2.24: shared saved filter views + the id of the one currently applied ("" =
@@ -264,7 +267,7 @@ export class MaintenanceSupporterPanel extends LitElement {
@state() private _groups: Record<string, MaintenanceGroup> = {};
@state() private _detailStatsData: Map<string, StatisticsPoint[]> = new Map();
@state() private _miniStatsData: Map<string, StatisticsPoint[]> = new Map();
@state() private _features: AdvancedFeatures = { adaptive: false, seasonal: false, environmental: false, budget: false, groups: false, checklists: false, schedule_time: false, completion_actions: false };
@state() private _features: AdvancedFeatures = { adaptive: false, seasonal: false, environmental: false, budget: false, groups: false, checklists: false, schedule_time: false, completion_actions: false, places: false };
// HA user IDs (UUIDs) granted full panel access despite not being HA admins.
@state() private _adminPanelUserIds: string[] = [];
// v2.8.4: master switch — the allowlist only grants the full panel when this
@@ -1087,7 +1090,7 @@ export class MaintenanceSupporterPanel extends LitElement {
const task = this._selectedEntryId && this._selectedTaskId
? this._getTask(this._selectedEntryId, this._selectedTaskId)
: null;
const entityId = task?.trigger_config?.entity_id;
const entityId = primaryTriggerEntity(task?.trigger_config);
if (entityId) {
// Drop the stale-range series so the chart shows its loading state.
const updated = new Map(this._detailStatsData);
@@ -1111,7 +1114,8 @@ export class MaintenanceSupporterPanel extends LitElement {
const entities: Array<{ entityId: string; isCounter: boolean }> = [];
for (const obj of this._objects) {
for (const task of obj.tasks) {
const entityId = task.trigger_config?.entity_id;
// The battery fleet's row shows "N to replace", not a sparkline.
const entityId = task.battery_fleet_task ? undefined : primaryTriggerEntity(task.trigger_config);
if (!entityId) continue;
entities.push({ entityId, isCounter: this._isCounterEntity(task.trigger_config) });
}
@@ -1217,6 +1221,9 @@ export class MaintenanceSupporterPanel extends LitElement {
// Label filter (v2.26 — also captured by saved views)
if (this._filterLabel && !(task.labels || []).includes(this._filterLabel)) continue;
// Place filter (2026-10): the object's zone, "home" for none.
if (this._filterPlace && (obj.object.place || "home") !== this._filterPlace) continue;
// Priority filter (#134 — also captured by saved views). Tasks
// without an explicit priority are "normal", the model default.
if (this._filterPriority && (task.priority || "normal") !== this._filterPriority) continue;
@@ -1353,8 +1360,9 @@ export class MaintenanceSupporterPanel extends LitElement {
if (s.msp_view === "task" && s.msp_entry && s.msp_task) {
this._historyFilter = null;
const task = this._getTask(s.msp_entry, s.msp_task);
if (task?.trigger_config?.entity_id) {
this._fetchDetailStats(task.trigger_config.entity_id, this._isCounterEntity(task.trigger_config));
const entityId = primaryTriggerEntity(task?.trigger_config);
if (task && entityId) {
this._fetchDetailStats(entityId, this._isCounterEntity(task.trigger_config));
}
}
}
@@ -1454,6 +1462,16 @@ export class MaintenanceSupporterPanel extends LitElement {
return [...seen].sort((a, b) => a.localeCompare(b));
}
/** 2026-10 places: the zones objects are maintained at, for the filter. */
private get _allPlaces(): { id: string; name: string }[] {
const seen = new Map<string, string>();
for (const obj of this._objects) {
const o = obj.object;
if (o.place && !seen.has(o.place)) seen.set(o.place, o.place_name || o.place);
}
return [...seen].map(([id, name]) => ({ id, name })).sort((a, b) => a.name.localeCompare(b.name));
}
/** The panel's current task-list filter state, in the shape a view stores. */
private get _currentFilters(): SavedViewFilters {
return {
@@ -1615,8 +1633,9 @@ export class MaintenanceSupporterPanel extends LitElement {
this._resetStickyPane();
this._fetchFullHistory(entryId, taskId);
const task = this._getTask(entryId, taskId);
if (task?.trigger_config?.entity_id) {
this._fetchDetailStats(task.trigger_config.entity_id, this._isCounterEntity(task.trigger_config));
const entityId = primaryTriggerEntity(task?.trigger_config);
if (task && entityId) {
this._fetchDetailStats(entityId, this._isCounterEntity(task.trigger_config));
}
return;
}
@@ -1637,10 +1656,9 @@ export class MaintenanceSupporterPanel extends LitElement {
// Lazy-load statistics for the task's trigger entity
const task = this._getTask(entryId, taskId);
if (task?.trigger_config?.entity_id) {
const entityId = task.trigger_config.entity_id;
const isCounter = this._isCounterEntity(task.trigger_config);
this._fetchDetailStats(entityId, isCounter);
const entityId = primaryTriggerEntity(task?.trigger_config);
if (task && entityId) {
this._fetchDetailStats(entityId, this._isCounterEntity(task.trigger_config));
}
}
@@ -3196,6 +3214,7 @@ export class MaintenanceSupporterPanel extends LitElement {
<maintenance-object-dialog
.hass=${this.hass}
.objects=${this._objects}
.placesEnabled=${this._features.places}
@object-saved=${this._onObjectSaved}
@object-replaced=${this._onObjectReplaced}
></maintenance-object-dialog>
@@ -3543,6 +3562,7 @@ export class MaintenanceSupporterPanel extends LitElement {
(this._filterStatus ? 1 : 0) +
(this._filterUser ? 1 : 0) +
(this._filterLabel ? 1 : 0) +
(this._filterPlace ? 1 : 0) +
(this._filterPriority ? 1 : 0) +
(this._activeViewId ? 1 : 0);
@@ -3626,6 +3646,24 @@ export class MaintenanceSupporterPanel extends LitElement {
</select>
</label>
` : nothing}
${this._features.places && this._allPlaces.length > 0 ? html`
<label class="filter-field">
<span class="filter-label">${t("filter_place", L)}</span>
<select
.value=${this._filterPlace}
@change=${(e: Event) => {
this._filterPlace = (e.target as HTMLSelectElement).value;
this._activeViewId = "";
}}
>
<option value="">${t("filter_place_all", L)}</option>
<option value="home" ?selected=${this._filterPlace === "home"}>${t("place_home", L)}</option>
${this._allPlaces.map(
(pl) => html`<option value=${pl.id} ?selected=${this._filterPlace === pl.id}>${pl.name}</option>`
)}
</select>
</label>
` : nothing}
<label class="filter-field">
<span class="filter-label">${t("priority", L)}</span>
<select
@@ -4743,6 +4781,13 @@ export class MaintenanceSupporterPanel extends LitElement {
<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}
${o.place
? html`<p class="meta place-meta">
<ha-icon icon="mdi:map-marker-outline"></ha-icon>
${t("filter_place", L)}: ${o.place_name || o.place}${o.remind_on_site ? html` · ${t("remind_on_site", L)}` : nothing}
${o.place_missing ? html`<span class="place-missing-badge">${t("place_missing", L)}</span>` : nothing}
</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>
@@ -963,6 +963,14 @@ export const panelStyles = css`
color: var(--info-color, #2196f3); font-weight: 500;
}
.paused-meta ha-icon { --mdc-icon-size: 16px; }
/* 2026-10 places: the object's place on its detail page. */
.place-meta {
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
.place-meta ha-icon { --mdc-icon-size: 16px; color: var(--secondary-text-color); }
.place-missing-badge {
font-size: 12px; color: var(--warning-color, #c77700);
}
.objects-table .oc-notes {
max-width: 220px;
overflow: hidden;
@@ -16,6 +16,7 @@ import { t, formatDate, formatDueDays, formatNumber } from "../styles";
import { px } from "./chart-utils";
import { daysProgress } from "../helpers/interval";
import { stampMs } from "../helpers/ha-time";
import { primaryTriggerEntity } from "../helpers/trigger-entities";
import type { MaintenanceTask, TaskRow, StatisticsPoint } from "../types";
const MINI_SPARKLINE_W = 60;
@@ -36,7 +37,9 @@ export function computeTrend(
miniStatsData: Map<string, StatisticsPoint[]>,
): TrendState | null {
const tc = row.trigger_config;
if (!tc?.entity_id) return null;
const entityId = primaryTriggerEntity(tc);
// The battery fleet's row tells "N to replace", not one battery's curve.
if (!tc || !entityId || row.battery_fleet_task) return null;
let toward: 1 | -1;
const type = tc.type || "threshold";
if (type === "threshold") {
@@ -48,7 +51,7 @@ export function computeTrend(
} else {
return null; // compound: conditions may point both ways
}
const statsPoints = miniStatsData.get(tc.entity_id) || [];
const statsPoints = miniStatsData.get(entityId) || [];
// Runtime and state_change accumulate SINCE the last completion — the
// history's trigger_value is the count at the previous trigger, a point
// from before the reset. Falling back to it read a freshly reset task
@@ -235,8 +238,9 @@ export function renderMiniSparkline(
miniStatsData: Map<string, StatisticsPoint[]>,
lang: string,
) {
if (!row.trigger_config?.entity_id) return nothing;
const entityId = row.trigger_config.entity_id;
const entityId = primaryTriggerEntity(row.trigger_config);
// The battery fleet's row tells "N to replace", not one battery's curve.
if (!entityId || row.battery_fleet_task) return nothing;
// PRIMARY: HA recorder statistics (daily, last 14 days)
const statsPoints = miniStatsData.get(entityId) || [];
@@ -16,6 +16,7 @@ import { t, fireMoreInfo, formatDueDays } from "../styles";
import { fmtNum, fmtVal } from "./chart-utils";
import { dueDaysOf } from "./progress";
import { stampMs } from "../helpers/ha-time";
import { primaryTriggerEntity, triggerEntityIds } from "../helpers/trigger-entities";
import "../components/trigger-chart";
import type { ChartPoint, ChartEvent } from "../components/trigger-chart";
import type { MaintenanceTask, TriggerConfig, StatisticsPoint } from "../types";
@@ -156,9 +157,9 @@ export function renderTriggerSection(task: MaintenanceTask, ctx: SparklineContex
const L = ctx.lang;
const info = task.trigger_entity_info;
const infos = task.trigger_entity_infos;
const friendlyName = info?.friendly_name || tc.entity_id || "—";
const entityId = tc.entity_id || "";
const entityIds = tc.entity_ids || (entityId ? [entityId] : []);
const entityId = primaryTriggerEntity(tc) || "";
const friendlyName = info?.friendly_name || entityId || "—";
const entityIds = triggerEntityIds(tc);
const unit = info?.unit_of_measurement || "";
const currentVal = task.trigger_current_value;
const triggerType = tc.type || "threshold";
@@ -215,7 +216,7 @@ export function renderTriggerSection(task: MaintenanceTask, ctx: SparklineContex
${triggerType === "compound" ? html`
<span class="trigger-limit-item"><span class="dot warn" aria-hidden="true"></span> ${t("compound_logic", L)}: ${tc.compound_logic || (tc as any).operator || "AND"}</span>
${(tc.conditions || []).map((cond: any, i: number) => html`
<span class="trigger-limit-item"><span class="dot range" aria-hidden="true"></span> ${i + 1}. ${t(cond.type === "due_date" ? "trigger_type_due_date" : cond.type || "unknown", L)}: ${cond.entity_id ? html`<span class="entity-link" @click=${(ev: Event) => fireMoreInfo(ev, cond.entity_id)}>${cond.entity_id}</span>` : ""}</span>
<span class="trigger-limit-item"><span class="dot range" aria-hidden="true"></span> ${i + 1}. ${t(cond.type === "due_date" ? "trigger_type_due_date" : cond.type || "unknown", L)}: ${triggerEntityIds(cond).map((eid, j) => html`${j > 0 ? ", " : ""}<span class="entity-link" @click=${(ev: Event) => fireMoreInfo(ev, eid)}>${eid}</span>`)}</span>
`)}
` : nothing}
</div>
@@ -330,7 +331,7 @@ function rawStatsPoints(task: MaintenanceTask, ctx: SparklineContext): ChartPoin
const tc = task.trigger_config;
if (!tc) return [];
const triggerType = tc.type || "threshold";
const entityId = tc.entity_id || "";
const entityId = primaryTriggerEntity(tc) || "";
// Runtime accumulates hours DERIVED from the entity's on/off time — the
// entity's own long-term statistics (an on/off ratio, or an unrelated raw
// sensor value) are NOT that accumulation. Plot only the recorded per-cycle
@@ -372,7 +373,7 @@ function renderChart(task: MaintenanceTask, unit: string, ctx: SparklineContext)
const tc = task.trigger_config;
if (!tc) return nothing;
const triggerType = tc.type || "threshold";
const entityId = tc.entity_id || "";
const entityId = primaryTriggerEntity(tc) || "";
// A due date only counts down — no curve to tell.
if (triggerType === "due_date") return nothing;
@@ -40,6 +40,13 @@ export interface MaintenanceObject {
// 2.19: attach to an existing HA device / nest under another object
ha_device_id?: string | null;
parent_entry_id?: string | null;
/** 2026-10 places: an HA zone the object is maintained at (null = home),
* its name, whether the zone is gone (then: as at home), and whether the
* object's reminders wait until somebody is there. */
place?: string | null;
place_name?: string | null;
place_missing?: boolean;
remind_on_site?: boolean;
/** v1.4.0 (#43): optional link to PDF manual / vendor page for the object */
documentation_url?: string | null;
/** v1.4.10 (#46): free-form notes — part numbers, procedures, etc. */
@@ -550,6 +557,8 @@ export interface AdvancedFeatures {
schedule_time: boolean;
/** v1.3.0: gates per-task on_complete_action + quick_complete_defaults UI. */
completion_actions: boolean;
/** 2026-10: an object's place (an HA zone) and "remind only on site". */
places: boolean;
}
/** A single point in a recorder statistics time series. */