From 8c53907c88aea9660f8507dc7c2b800a1e836c67 Mon Sep 17 00:00:00 2001 From: Home Assistant Version Control Date: Fri, 31 Jul 2026 16:29:09 +0000 Subject: [PATCH] 91 files --- .../maintenance_supporter/__init__.py | 165 +++++- .../maintenance_supporter/config_flow.py | 2 +- .../entity/entity_base.py | 62 ++- .../components/battery-fleet-section.ts | 85 +++ .../frontend-src/locales/cs.json | 6 + .../frontend-src/locales/da.json | 6 + .../frontend-src/locales/de.json | 6 + .../frontend-src/locales/en.json | 6 + .../frontend-src/locales/es.json | 6 + .../frontend-src/locales/fi.json | 6 + .../frontend-src/locales/fr.json | 6 + .../frontend-src/locales/hi.json | 6 + .../frontend-src/locales/hu.json | 6 + .../frontend-src/locales/it.json | 6 + .../frontend-src/locales/ja.json | 6 + .../frontend-src/locales/ko.json | 6 + .../frontend-src/locales/nb.json | 6 + .../frontend-src/locales/nl.json | 6 + .../frontend-src/locales/pl.json | 6 + .../frontend-src/locales/pt-br.json | 6 + .../frontend-src/locales/pt.json | 6 + .../frontend-src/locales/ru.json | 6 + .../frontend-src/locales/sv.json | 6 + .../frontend-src/locales/tr.json | 6 + .../frontend-src/locales/uk.json | 6 + .../frontend-src/locales/zh.json | 6 + .../frontend-src/maintenance-calendar-card.ts | 50 +- .../frontend/__init__.py | 4 +- .../frontend/locales/cs.json | 6 + .../frontend/locales/da.json | 6 + .../frontend/locales/de.json | 6 + .../frontend/locales/en.json | 6 + .../frontend/locales/es.json | 6 + .../frontend/locales/fi.json | 6 + .../frontend/locales/fr.json | 6 + .../frontend/locales/hi.json | 6 + .../frontend/locales/hu.json | 6 + .../frontend/locales/it.json | 6 + .../frontend/locales/ja.json | 6 + .../frontend/locales/ko.json | 6 + .../frontend/locales/nb.json | 6 + .../frontend/locales/nl.json | 6 + .../frontend/locales/pl.json | 6 + .../frontend/locales/pt-br.json | 6 + .../frontend/locales/pt.json | 6 + .../frontend/locales/ru.json | 6 + .../frontend/locales/sv.json | 6 + .../frontend/locales/tr.json | 6 + .../frontend/locales/uk.json | 6 + .../frontend/locales/zh.json | 6 + .../frontend/maintenance-calendar-card.js | 95 ++-- .../frontend/maintenance-card.js | 44 +- .../frontend/maintenance-panel.js | 520 ++++++++++-------- .../frontend/maintenance-strategy-shim.js | 2 +- ...7WI.js => budget-section-card-HV3ADEV3.js} | 4 +- .../{chunk-RLJYCMAK.js => chunk-DM35MPY4.js} | 4 +- .../{chunk-KRBXISES.js => chunk-JSL2OXKU.js} | 2 +- .../{chunk-TF44IC56.js => chunk-V27MBWM3.js} | 4 +- ...t-UUGV3XIE.js => dialog-mount-JGWDXBYE.js} | 10 +- ...GJU.js => groups-section-card-BPWR5VFS.js} | 4 +- ...O.js => vacation-section-card-CTBYKBDH.js} | 4 +- .../maintenance-dashboard-strategy.js | 6 +- .../helpers/battery_fleet.py | 26 +- .../helpers/battery_fleet_setup.py | 107 ++++ .../maintenance_supporter/manifest.json | 2 +- .../maintenance_supporter/panel.py | 9 +- .../maintenance_supporter/strings.json | 4 + .../translations/cs.json | 4 + .../translations/da.json | 4 + .../translations/de.json | 4 + .../translations/en.json | 4 + .../translations/es.json | 4 + .../translations/fi.json | 4 + .../translations/fr.json | 4 + .../translations/hi.json | 4 + .../translations/hu.json | 4 + .../translations/it.json | 4 + .../translations/ja.json | 4 + .../translations/ko.json | 4 + .../translations/nb.json | 4 + .../translations/nl.json | 4 + .../translations/pl.json | 4 + .../translations/pt-BR.json | 4 + .../translations/pt.json | 4 + .../translations/ru.json | 4 + .../translations/sv.json | 4 + .../translations/tr.json | 4 + .../translations/uk.json | 4 + .../translations/zh-Hans.json | 4 + .../websocket/battery_fleet.py | 6 + zigbee2mqtt/state.json | 46 +- 91 files changed, 1232 insertions(+), 387 deletions(-) rename custom_components/maintenance_supporter/frontend/strategy/chunks/{budget-section-card-PAADD7WI.js => budget-section-card-HV3ADEV3.js} (97%) rename custom_components/maintenance_supporter/frontend/strategy/chunks/{chunk-RLJYCMAK.js => chunk-DM35MPY4.js} (95%) rename custom_components/maintenance_supporter/frontend/strategy/chunks/{chunk-KRBXISES.js => chunk-JSL2OXKU.js} (94%) rename custom_components/maintenance_supporter/frontend/strategy/chunks/{chunk-TF44IC56.js => chunk-V27MBWM3.js} (95%) rename custom_components/maintenance_supporter/frontend/strategy/chunks/{dialog-mount-UUGV3XIE.js => dialog-mount-JGWDXBYE.js} (99%) rename custom_components/maintenance_supporter/frontend/strategy/chunks/{groups-section-card-RVBRIGJU.js => groups-section-card-BPWR5VFS.js} (97%) rename custom_components/maintenance_supporter/frontend/strategy/chunks/{vacation-section-card-PLHOVD6O.js => vacation-section-card-CTBYKBDH.js} (97%) diff --git a/custom_components/maintenance_supporter/__init__.py b/custom_components/maintenance_supporter/__init__.py index a2d621d..818f1d9 100644 --- a/custom_components/maintenance_supporter/__init__.py +++ b/custom_components/maintenance_supporter/__init__.py @@ -819,6 +819,44 @@ async def _async_setup_shared(hass: HomeAssistant) -> bool: # registered in async_setup_entry. Together these two listeners keep the # dashboard area and the HA device area in sync after the initial creation # (DeviceInfo.suggested_area only fires once, on first device creation). + @callback + def _object_entries_for_device( + hass: HomeAssistant, device: dr.DeviceEntry + ) -> list[ConfigEntry]: + """Our object entries that live on this device. + + Two ways an object reaches a device page, and they need different + lookups: + + * its OWN device — our config entry is listed on it; + * a LINKED device owned by another integration — we deliberately do + NOT add our config entry there any more (HA 2026.8 forbids it), so + the only record of the link is ``obj["ha_device_id"]``. Matching on + ``device.config_entries`` alone would silently stop syncing exactly + the objects the user attached by hand. + + Several objects may point at the same device; all of them follow it. + """ + found: list[ConfigEntry] = [] + seen: set[str] = set() + # 2026.8 gives a device exactly one config entry (`config_entry_id`); + # the plural `config_entries` is a silently-deprecated compat shim + # there — no report_usage, so the deprecation gate cannot see it — and + # the only spelling 2026.7 has. Prefer the singular where it exists. + single = getattr(device, "config_entry_id", None) + owner_ids = (single,) if single is not None else tuple(getattr(device, "config_entries", ()) or ()) + for ce_id in owner_ids: + ce = hass.config_entries.async_get_entry(ce_id) + if ce is not None and ce.domain == DOMAIN and ce.unique_id != GLOBAL_UNIQUE_ID: + found.append(ce) + seen.add(ce.entry_id) + for ce in hass.config_entries.async_entries(DOMAIN): + if ce.entry_id in seen or ce.unique_id == GLOBAL_UNIQUE_ID: + continue + if (ce.data.get(CONF_OBJECT) or {}).get("ha_device_id") == device.id: + found.append(ce) + return found + @callback def _on_device_registry_update( event: Event[dr.EventDeviceRegistryUpdatedData], @@ -832,17 +870,12 @@ async def _async_setup_shared(hass: HomeAssistant) -> bool: device = dr.async_get(hass).async_get(device_id) if device is None: return - for ce_id in device.config_entries: - ce = hass.config_entries.async_get_entry(ce_id) - if ce is None or ce.domain != DOMAIN or ce.unique_id == GLOBAL_UNIQUE_ID: - continue + for ce in _object_entries_for_device(hass, device): obj = dict(ce.data.get(CONF_OBJECT, {})) if obj.get("area_id") == device.area_id: - return # already in sync — break the loop with the forward listener + continue # already in sync — breaks the loop with the forward listener obj["area_id"] = device.area_id - new_data = {**ce.data, CONF_OBJECT: obj} - hass.config_entries.async_update_entry(ce, data=new_data) - return + hass.config_entries.async_update_entry(ce, data={**ce.data, CONF_OBJECT: obj}) unsub_device = hass.bus.async_listen(dr.EVENT_DEVICE_REGISTRY_UPDATED, _on_device_registry_update) @@ -1014,7 +1047,7 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: rotation tasks that were configured without an initial assignee — those were invisible to every user filter until their first completion. """ - if entry.version > 1 or entry.minor_version >= 4: + if entry.version > 1 or entry.minor_version >= 5: return True is_object_entry = entry.unique_id != GLOBAL_UNIQUE_ID @@ -1073,6 +1106,19 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: data[CONF_TASKS] = new_tasks minor = 4 + # 4 → 5: stop co-owning a linked device. Until now, attaching an object to + # an existing device also put OUR config entry on that device. Home + # Assistant forbids that from 2026.8 and splits such a device into one per + # config entry on upgrade — leaving a nameless extra device holding our + # entities while the appliance's page shows none. Removing the association + # here means the split never has anything to split. + if minor < 5: + if is_object_entry and (device_id := (data.get(CONF_OBJECT) or {}).get("ha_device_id")): + from .helpers.device_link import shed_owned_devices + + shed_owned_devices(hass, own_entry_id=entry.entry_id, source_device_id=device_id) + minor = 5 + hass.config_entries.async_update_entry(entry, data=data, minor_version=minor) _LOGGER.info("Migrated entry %s to minor_version %s", entry.entry_id, minor) return True @@ -1088,6 +1134,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: MaintenanceSupporterConf await _async_setup_shared(hass) is_global = entry.unique_id == GLOBAL_UNIQUE_ID + # True once the object's stored device link resolved to a live device this + # setup — drives the post-platform cleanup of empty leftover own devices. + linked_device_live = False if is_global: # Global entry: no per-object coordinator, but a summary coordinator @@ -1189,6 +1238,81 @@ async def async_setup_entry(hass: HomeAssistant, entry: MaintenanceSupporterConf ) await store.async_save() + # A device link stored before HA 2026.8 may now point at a + # pre-migration composite id, which names no registered device — HA + # refuses to attach entities to it and tells the user to file a bug + # against us. Repoint it at the surviving device and write that back, + # so the repair happens once rather than on every start. + obj_data = entry.data.get(CONF_OBJECT) or {} + if stored_device_id := obj_data.get("ha_device_id"): + from .helpers.device_link import ( + has_own_devices, + resolve_linked_device_id, + shed_owned_devices, + ) + + resolved = resolve_linked_device_id(hass, stored_device_id, own_entry_id=entry.entry_id) + if resolved and resolved != stored_device_id: + _LOGGER.info( + "Object %s was linked to device %s, which the 2026.8 split replaced; repointed to %s", + entry.title, + stored_device_id, + resolved, + ) + hass.config_entries.async_update_entry( + entry, + data={**entry.data, CONF_OBJECT: {**obj_data, "ha_device_id": resolved}}, + ) + # An id we cannot resolve is deliberately LEFT ALONE. Clearing it + # would gain nothing — a missing device already falls back to an own + # device at render time — and would destroy the user's choice for + # good. The id may well be meaningful again later: a JSON export + # carries it to a foreign instance where it dangles until the backup + # comes home, and a device can return when its integration is + # re-added or re-enabled. + # + # Falling back is quiet, though, and a link the user set on purpose + # is worth telling them about rather than letting the object drift + # onto a device of its own unannounced. The issue clears itself the + # moment the link resolves again. + from homeassistant.helpers import issue_registry as ir + + issue_id = f"device_link_lost_{entry.entry_id}" + if resolved is None: + ir.async_create_issue( + hass, + DOMAIN, + issue_id, + is_fixable=False, + severity=ir.IssueSeverity.WARNING, + translation_key="device_link_lost", + translation_placeholders={"object": obj_data.get("name") or entry.title}, + ) + else: + ir.async_delete_issue(hass, DOMAIN, issue_id) + + # A linked object owns no device. If it does, this install predates + # the rework — either it co-owned the appliance's device (split on + # upgrade) or it forked one carrying the appliance's identity. Hand + # that to Home Assistant's own repair, which also relinks the + # entities, rather than deleting devices by hand. + if has_own_devices(hass, entry.entry_id): + _LOGGER.info("Shedding device(s) %s should not own after linking", entry.title) + shed_owned_devices(hass, own_entry_id=entry.entry_id, source_device_id=resolved) + linked_device_live = resolved is not None + + # #115: the battery fleet's seeded name/notes/part texts are stored + # snapshots — a fleet set up before localized seeding existed (or under + # a different UI language) keeps English notes forever while every + # runtime string around them is translated. Rewrite the ones the user + # never touched into the instance's language; edited texts stay. + if obj_data.get("battery_fleet"): + from .helpers.battery_fleet_setup import retranslate_seeded_texts + from .helpers.i18n import normalize_language + + if retranslate_seeded_texts(hass, entry, normalize_language(hass)): + _LOGGER.info("Retranslated the battery fleet's seeded texts for %s", entry.title) + coordinator = MaintenanceCoordinator(hass, entry, store) entry.runtime_data = MaintenanceSupporterData(coordinator=coordinator, store=store) await coordinator.async_config_entry_first_refresh() @@ -1222,6 +1346,22 @@ async def async_setup_entry(hass: HomeAssistant, entry: MaintenanceSupporterConf ) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + + if linked_device_live: + # The entities of a linked object live on the appliance'''s device, so a + # device of our own with nothing on it is a leftover — of the object'''s + # earlier unlinked life, or of the 2026.8 split — and shows up as an + # empty duplicate named after the object. The targeted shed above only + # removes duplicates of the SOURCE device, so an old own device (our + # identifiers, not a fork) survives it on both HA versions. Decidable + # only now: the platform setup above has re-attached the entities. + dev_reg = dr.async_get(hass) + ent_reg = er.async_get(hass) + for device in dr.async_entries_for_config_entry(dev_reg, entry.entry_id): + if not er.async_entries_for_device(ent_reg, device.id, include_disabled_entities=True): + _LOGGER.info("Removing empty leftover device %s of linked object %s", device.id, entry.title) + dev_reg.async_remove_device(device.id) + return True @@ -1517,6 +1657,13 @@ async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: # as a fixable repair issue so the user can restore it (#86). _sync_missing_global_entry_issue(hass) return + + # A "lost its device link" notice belongs to the object; deleting the object + # answers it, so it must not outlive it as an orphan in the Repairs list. + from homeassistant.helpers import issue_registry as ir + + ir.async_delete_issue(hass, DOMAIN, f"device_link_lost_{entry.entry_id}") + # #111: hand any shared spare-part pool to a borrower BEFORE the Store goes # — the stock numbers exist nowhere else. Must happen here rather than in # the panel's delete: HA's own Configure-UI removal never reaches that path. diff --git a/custom_components/maintenance_supporter/config_flow.py b/custom_components/maintenance_supporter/config_flow.py index a151f84..f6b1bd8 100644 --- a/custom_components/maintenance_supporter/config_flow.py +++ b/custom_components/maintenance_supporter/config_flow.py @@ -84,7 +84,7 @@ class MaintenanceSupporterConfigFlow(TriggerConfigMixin, ConfigFlow, domain=DOMA """Handle a config flow for Maintenance Supporter.""" VERSION = 1 - MINOR_VERSION = 4 + MINOR_VERSION = 5 def __init__(self) -> None: """Initialize the config flow.""" diff --git a/custom_components/maintenance_supporter/entity/entity_base.py b/custom_components/maintenance_supporter/entity/entity_base.py index c654fb9..388a479 100644 --- a/custom_components/maintenance_supporter/entity/entity_base.py +++ b/custom_components/maintenance_supporter/entity/entity_base.py @@ -10,6 +10,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from ..const import CONF_OBJECT, DOMAIN, GLOBAL_UNIQUE_ID from ..coordinator import MaintenanceCoordinator +from ..helpers import device_link class MaintenanceEntity(CoordinatorEntity[MaintenanceCoordinator]): @@ -27,16 +28,49 @@ class MaintenanceEntity(CoordinatorEntity[MaintenanceCoordinator]): self._task_id = task_id self._object_data = coordinator.entry.data.get(CONF_OBJECT, {}) + # 2.19 device attachment — an object can live on an EXISTING device + # owned by another integration. Home Assistant's documented way to do + # that is to point the entity at the device and nothing else: + # https://developers.home-assistant.io/blog/2025/07/18/updated-pattern-for-helpers-linking-to-devices/ + # + # We used to return the device's own identifiers from `device_info`, + # which made the registry merge our config entry onto it. HA 2026.8 + # scopes identifiers PER CONFIG ENTRY, so that no longer merges — it + # creates a second, nameless device holding our entities while the + # appliance's page shows none of them. Setting `device_entry` attaches + # the entities without claiming any ownership, and works on 2026.7 too + # (`entity_platform` uses a pre-set value when `device_info` is None). + # + # Resolved ONCE here so `device_info` below and the entity's device + # cannot disagree; re-evaluated when the entry reloads, which a + # link change already triggers. + # + # Resolution goes through helpers.device_link, NOT a raw + # `async_get(stored_id)`: on HA 2026.8 `async_get` still ANSWERS for a + # pre-migration composite id by synthesising a read-only device that + # carries that dead id — attaching to it makes the entity registry + # refuse the link and log a "please create a bug report" warning. The + # resolver returns a live id or None, never the synthetic. (Called via + # the module so tests can substitute the resolver.) + self._linked_device: dr.DeviceEntry | None = None + if device_id := self._object_data.get("ha_device_id"): + resolved = device_link.resolve_linked_device_id( + coordinator.hass, device_id, own_entry_id=coordinator.entry.entry_id + ) + if resolved is not None: + self._linked_device = dr.async_get(coordinator.hass).async_get(resolved) + if self._linked_device is not None: + self.device_entry = self._linked_device + @property - def device_info(self) -> DeviceInfo: + def device_info(self) -> DeviceInfo | None: """Return device info for this maintenance object. Three shapes (2.19, device attachment): - * ``ha_device_id`` set → attach to that EXISTING device: return only - its identifiers/connections so the registry merges us onto it — no - name/manufacturer, which would overwrite the owning integration's - metadata (the obj→device forward-sync skips linked objects too). + * linked to an existing device → ``None``. The entity is already + attached via ``device_entry`` (see ``__init__``); describing a device + here would create one of our own instead. * ``parent_entry_id`` set → our own device, nested under the parent object's device via ``via_device``. * neither → our own stand-alone device (the historical shape). @@ -44,17 +78,13 @@ class MaintenanceEntity(CoordinatorEntity[MaintenanceCoordinator]): obj = self._object_data hass = self.coordinator.hass - if device_id := obj.get("ha_device_id"): - device = dr.async_get(hass).async_get(device_id) - if device is not None and (device.identifiers or device.connections): - info = DeviceInfo() - if device.identifiers: - info["identifiers"] = device.identifiers - if device.connections: - info["connections"] = device.connections - return info - # Linked device vanished → fall through to an own device so the - # entities never end up device-less. + if self._linked_device is not None: + return None + # A linked device that no longer exists falls through to an own device, + # so the entities are never left device-less. Core helpers drop to no + # device at all here; keeping the fallback preserves what this + # integration already did, and an object with its own device page reads + # better than one with none. identifiers = {(DOMAIN, self.coordinator.entry.unique_id or "")} diff --git a/custom_components/maintenance_supporter/frontend-src/components/battery-fleet-section.ts b/custom_components/maintenance_supporter/frontend-src/components/battery-fleet-section.ts index dc020a0..7bffea4 100644 --- a/custom_components/maintenance_supporter/frontend-src/components/battery-fleet-section.ts +++ b/custom_components/maintenance_supporter/frontend-src/components/battery-fleet-section.ts @@ -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; needs_soon: Record; 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 { return Object.entries(needs) .map(([type, qty]) => `${qty}× ${type}`) @@ -220,6 +236,41 @@ export class MaintenanceBatteryFleetSection extends LitElement { ` : nothing} + ${ov.all?.length + ? html` +
+ ${t("battery_fleet_all", L)} (${ov.all.length}) +
+ ${ov.all.map( + (b) => html` +
+ ${b.device_name} + ${t("battery_fleet_status_" + b.status, L)} + ${b.quantity}× ${b.battery_type} + ${b.level != null ? html`${b.level}%` : nothing} + ${b.days_until != null + ? html`~${this._predictedDate(b.days_until)}` + : nothing} + +
+ `, + )} +
+
${t("battery_fleet_all_hint", L)}
+
+ ` + : nothing} ${ov.excluded?.length ? html`
@@ -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); diff --git a/custom_components/maintenance_supporter/frontend-src/locales/cs.json b/custom_components/maintenance_supporter/frontend-src/locales/cs.json index 5f27b76..5d3e9a4 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/cs.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/cs.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/da.json b/custom_components/maintenance_supporter/frontend-src/locales/da.json index ca6fffb..631681f 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/da.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/da.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/de.json b/custom_components/maintenance_supporter/frontend-src/locales/de.json index 4522d34..fcf3ebb 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/de.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/de.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/en.json b/custom_components/maintenance_supporter/frontend-src/locales/en.json index 6efa0f5..556a52b 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/en.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/en.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/es.json b/custom_components/maintenance_supporter/frontend-src/locales/es.json index 0827d83..49be220 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/es.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/es.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/fi.json b/custom_components/maintenance_supporter/frontend-src/locales/fi.json index 7125924..91051ee 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/fi.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/fi.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/fr.json b/custom_components/maintenance_supporter/frontend-src/locales/fr.json index 521d2f1..69bdcad 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/fr.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/fr.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/hi.json b/custom_components/maintenance_supporter/frontend-src/locales/hi.json index 2407e7c..ba1c4b3 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/hi.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/hi.json @@ -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": "बैटरी फ्लीट सेट — एक कार्य सभी बैटरियों को ट्रैक करता है।", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/hu.json b/custom_components/maintenance_supporter/frontend-src/locales/hu.json index e7509a9..1dc6e6d 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/hu.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/hu.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/it.json b/custom_components/maintenance_supporter/frontend-src/locales/it.json index 5c5f541..dc0c3ea 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/it.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/it.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/ja.json b/custom_components/maintenance_supporter/frontend-src/locales/ja.json index ccba34d..c43e2af 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/ja.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/ja.json @@ -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つのタスクで全電池を管理。", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/ko.json b/custom_components/maintenance_supporter/frontend-src/locales/ko.json index b7bca14..6380498 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/ko.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/ko.json @@ -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": "배터리 플릿이 설정되었습니다 — 하나의 작업이 모든 배터리를 추적합니다.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/nb.json b/custom_components/maintenance_supporter/frontend-src/locales/nb.json index a26c937..f91fdad 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/nb.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/nb.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/nl.json b/custom_components/maintenance_supporter/frontend-src/locales/nl.json index ef26e06..a97385a 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/nl.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/nl.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/pl.json b/custom_components/maintenance_supporter/frontend-src/locales/pl.json index 404379a..01a1613 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/pl.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/pl.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/pt-br.json b/custom_components/maintenance_supporter/frontend-src/locales/pt-br.json index 0df136b..bfdf93b 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/pt-br.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/pt-br.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/pt.json b/custom_components/maintenance_supporter/frontend-src/locales/pt.json index bf175b6..c5e2510 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/pt.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/pt.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/ru.json b/custom_components/maintenance_supporter/frontend-src/locales/ru.json index b1640b8..7efafca 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/ru.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/ru.json @@ -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": "Парк батарей настроен — одна задача следит за всеми.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/sv.json b/custom_components/maintenance_supporter/frontend-src/locales/sv.json index 36b0eb6..1168695 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/sv.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/sv.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/tr.json b/custom_components/maintenance_supporter/frontend-src/locales/tr.json index 2b2c77c..24e86ea 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/tr.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/tr.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/uk.json b/custom_components/maintenance_supporter/frontend-src/locales/uk.json index 618873c..755186d 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/uk.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/uk.json @@ -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": "Парк батарей налаштовано — одне завдання стежить за всіма.", diff --git a/custom_components/maintenance_supporter/frontend-src/locales/zh.json b/custom_components/maintenance_supporter/frontend-src/locales/zh.json index 3a08169..00044ee 100644 --- a/custom_components/maintenance_supporter/frontend-src/locales/zh.json +++ b/custom_components/maintenance_supporter/frontend-src/locales/zh.json @@ -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": "电池群已设置 — 一个任务跟踪所有电池。", diff --git a/custom_components/maintenance_supporter/frontend-src/maintenance-calendar-card.ts b/custom_components/maintenance_supporter/frontend-src/maintenance-calendar-card.ts index 5d9cd4d..6a9e23c 100644 --- a/custom_components/maintenance_supporter/frontend-src/maintenance-calendar-card.ts +++ b/custom_components/maintenance_supporter/frontend-src/maintenance-calendar-card.ts @@ -20,7 +20,9 @@ * show_user_filter: true # default true * user_filter: "" # "" | "current_user" | "" * show_object_filter: true # default true; dropdown appears with 2+ objects - * object_filter: "" # "" | "" | "" (Discussion #83) + * object_filter: "" # "" | "" | "" — 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; }}> - ${[...this._objects] + ${[...base] .sort((a, b) => a.object.name.localeCompare(b.object.name)) .map( (o) => html``, @@ -569,7 +594,8 @@ class MaintenanceCalendarCardEditor extends LitElement { />
- 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.
`; diff --git a/custom_components/maintenance_supporter/frontend/__init__.py b/custom_components/maintenance_supporter/frontend/__init__.py index ff378d1..e451b1a 100644 --- a/custom_components/maintenance_supporter/frontend/__init__.py +++ b/custom_components/maintenance_supporter/frontend/__init__.py @@ -5,7 +5,9 @@ from __future__ import annotations import logging from pathlib import Path -from homeassistant.components.http import StaticPathConfig +# Valid at runtime on HA 2026.7 and 2026.8; see the note in ../panel.py for why +# the re-export is silenced instead of importing from a different path. +from homeassistant.components.http import StaticPathConfig # type: ignore[attr-defined] from homeassistant.core import HomeAssistant from ..const import ( diff --git a/custom_components/maintenance_supporter/frontend/locales/cs.json b/custom_components/maintenance_supporter/frontend/locales/cs.json index 5f27b76..5d3e9a4 100644 --- a/custom_components/maintenance_supporter/frontend/locales/cs.json +++ b/custom_components/maintenance_supporter/frontend/locales/cs.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/da.json b/custom_components/maintenance_supporter/frontend/locales/da.json index ca6fffb..631681f 100644 --- a/custom_components/maintenance_supporter/frontend/locales/da.json +++ b/custom_components/maintenance_supporter/frontend/locales/da.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/de.json b/custom_components/maintenance_supporter/frontend/locales/de.json index 4522d34..fcf3ebb 100644 --- a/custom_components/maintenance_supporter/frontend/locales/de.json +++ b/custom_components/maintenance_supporter/frontend/locales/de.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/en.json b/custom_components/maintenance_supporter/frontend/locales/en.json index 6efa0f5..556a52b 100644 --- a/custom_components/maintenance_supporter/frontend/locales/en.json +++ b/custom_components/maintenance_supporter/frontend/locales/en.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/es.json b/custom_components/maintenance_supporter/frontend/locales/es.json index 0827d83..49be220 100644 --- a/custom_components/maintenance_supporter/frontend/locales/es.json +++ b/custom_components/maintenance_supporter/frontend/locales/es.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/fi.json b/custom_components/maintenance_supporter/frontend/locales/fi.json index 7125924..91051ee 100644 --- a/custom_components/maintenance_supporter/frontend/locales/fi.json +++ b/custom_components/maintenance_supporter/frontend/locales/fi.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/fr.json b/custom_components/maintenance_supporter/frontend/locales/fr.json index 521d2f1..69bdcad 100644 --- a/custom_components/maintenance_supporter/frontend/locales/fr.json +++ b/custom_components/maintenance_supporter/frontend/locales/fr.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/hi.json b/custom_components/maintenance_supporter/frontend/locales/hi.json index 2407e7c..ba1c4b3 100644 --- a/custom_components/maintenance_supporter/frontend/locales/hi.json +++ b/custom_components/maintenance_supporter/frontend/locales/hi.json @@ -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": "बैटरी फ्लीट सेट — एक कार्य सभी बैटरियों को ट्रैक करता है।", diff --git a/custom_components/maintenance_supporter/frontend/locales/hu.json b/custom_components/maintenance_supporter/frontend/locales/hu.json index e7509a9..1dc6e6d 100644 --- a/custom_components/maintenance_supporter/frontend/locales/hu.json +++ b/custom_components/maintenance_supporter/frontend/locales/hu.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/it.json b/custom_components/maintenance_supporter/frontend/locales/it.json index 5c5f541..dc0c3ea 100644 --- a/custom_components/maintenance_supporter/frontend/locales/it.json +++ b/custom_components/maintenance_supporter/frontend/locales/it.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/ja.json b/custom_components/maintenance_supporter/frontend/locales/ja.json index ccba34d..c43e2af 100644 --- a/custom_components/maintenance_supporter/frontend/locales/ja.json +++ b/custom_components/maintenance_supporter/frontend/locales/ja.json @@ -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つのタスクで全電池を管理。", diff --git a/custom_components/maintenance_supporter/frontend/locales/ko.json b/custom_components/maintenance_supporter/frontend/locales/ko.json index b7bca14..6380498 100644 --- a/custom_components/maintenance_supporter/frontend/locales/ko.json +++ b/custom_components/maintenance_supporter/frontend/locales/ko.json @@ -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": "배터리 플릿이 설정되었습니다 — 하나의 작업이 모든 배터리를 추적합니다.", diff --git a/custom_components/maintenance_supporter/frontend/locales/nb.json b/custom_components/maintenance_supporter/frontend/locales/nb.json index a26c937..f91fdad 100644 --- a/custom_components/maintenance_supporter/frontend/locales/nb.json +++ b/custom_components/maintenance_supporter/frontend/locales/nb.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/nl.json b/custom_components/maintenance_supporter/frontend/locales/nl.json index ef26e06..a97385a 100644 --- a/custom_components/maintenance_supporter/frontend/locales/nl.json +++ b/custom_components/maintenance_supporter/frontend/locales/nl.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/pl.json b/custom_components/maintenance_supporter/frontend/locales/pl.json index 404379a..01a1613 100644 --- a/custom_components/maintenance_supporter/frontend/locales/pl.json +++ b/custom_components/maintenance_supporter/frontend/locales/pl.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/pt-br.json b/custom_components/maintenance_supporter/frontend/locales/pt-br.json index 0df136b..bfdf93b 100644 --- a/custom_components/maintenance_supporter/frontend/locales/pt-br.json +++ b/custom_components/maintenance_supporter/frontend/locales/pt-br.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/pt.json b/custom_components/maintenance_supporter/frontend/locales/pt.json index bf175b6..c5e2510 100644 --- a/custom_components/maintenance_supporter/frontend/locales/pt.json +++ b/custom_components/maintenance_supporter/frontend/locales/pt.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/ru.json b/custom_components/maintenance_supporter/frontend/locales/ru.json index b1640b8..7efafca 100644 --- a/custom_components/maintenance_supporter/frontend/locales/ru.json +++ b/custom_components/maintenance_supporter/frontend/locales/ru.json @@ -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": "Парк батарей настроен — одна задача следит за всеми.", diff --git a/custom_components/maintenance_supporter/frontend/locales/sv.json b/custom_components/maintenance_supporter/frontend/locales/sv.json index 36b0eb6..1168695 100644 --- a/custom_components/maintenance_supporter/frontend/locales/sv.json +++ b/custom_components/maintenance_supporter/frontend/locales/sv.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/tr.json b/custom_components/maintenance_supporter/frontend/locales/tr.json index 2b2c77c..24e86ea 100644 --- a/custom_components/maintenance_supporter/frontend/locales/tr.json +++ b/custom_components/maintenance_supporter/frontend/locales/tr.json @@ -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.", diff --git a/custom_components/maintenance_supporter/frontend/locales/uk.json b/custom_components/maintenance_supporter/frontend/locales/uk.json index 618873c..755186d 100644 --- a/custom_components/maintenance_supporter/frontend/locales/uk.json +++ b/custom_components/maintenance_supporter/frontend/locales/uk.json @@ -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": "Парк батарей налаштовано — одне завдання стежить за всіма.", diff --git a/custom_components/maintenance_supporter/frontend/locales/zh.json b/custom_components/maintenance_supporter/frontend/locales/zh.json index 3a08169..00044ee 100644 --- a/custom_components/maintenance_supporter/frontend/locales/zh.json +++ b/custom_components/maintenance_supporter/frontend/locales/zh.json @@ -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": "电池群已设置 — 一个任务跟踪所有电池。", diff --git a/custom_components/maintenance_supporter/frontend/maintenance-calendar-card.js b/custom_components/maintenance_supporter/frontend/maintenance-calendar-card.js index 1d4f088..1c5c438 100644 --- a/custom_components/maintenance_supporter/frontend/maintenance-calendar-card.js +++ b/custom_components/maintenance_supporter/frontend/maintenance-calendar-card.js @@ -1,7 +1,7 @@ -/*! maintenance_supporter frontend 2.44.1 */ -var at=Object.defineProperty;var nt=Object.getOwnPropertyDescriptor;var x=(a,e,t,o)=>{for(var r=o>1?void 0:o?nt(e,t):e,n=a.length-1,s;n>=0;n--)(s=a[n])&&(r=(o?s(e,t,r):s(r))||r);return o&&r&&at(e,t,r),r};var Z=globalThis,X=Z.ShadowRoot&&(Z.ShadyCSS===void 0||Z.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,ce=Symbol(),ke=new WeakMap,H=class{constructor(e,t,o){if(this._$cssResult$=!0,o!==ce)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o,t=this.t;if(X&&e===void 0){let o=t!==void 0&&t.length===1;o&&(e=ke.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),o&&ke.set(t,e))}return e}toString(){return this.cssText}},Se=a=>new H(typeof a=="string"?a:a+"",void 0,ce),k=(a,...e)=>{let t=a.length===1?a[0]:e.reduce((o,r,n)=>o+(s=>{if(s._$cssResult$===!0)return s.cssText;if(typeof s=="number")return s;throw Error("Value passed to 'css' function must be a 'css' function result: "+s+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(r)+a[n+1],a[0]);return new H(t,a,ce)},$e=(a,e)=>{if(X)a.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(let t of e){let o=document.createElement("style"),r=Z.litNonce;r!==void 0&&o.setAttribute("nonce",r),o.textContent=t.cssText,a.appendChild(o)}},de=X?a=>a:a=>a instanceof CSSStyleSheet?(e=>{let t="";for(let o of e.cssRules)t+=o.cssText;return Se(t)})(a):a;var{is:st,defineProperty:it,getOwnPropertyDescriptor:lt,getOwnPropertyNames:ct,getOwnPropertySymbols:dt,getPrototypeOf:pt}=Object,ee=globalThis,Ae=ee.trustedTypes,ut=Ae?Ae.emptyScript:"",_t=ee.reactiveElementPolyfillSupport,q=(a,e)=>a,I={toAttribute(a,e){switch(e){case Boolean:a=a?ut:null;break;case Object:case Array:a=a==null?a:JSON.stringify(a)}return a},fromAttribute(a,e){let t=a;switch(e){case Boolean:t=a!==null;break;case Number:t=a===null?null:Number(a);break;case Object:case Array:try{t=JSON.parse(a)}catch{t=null}}return t}},te=(a,e)=>!st(a,e),Ce={attribute:!0,type:String,converter:I,reflect:!1,useDefault:!1,hasChanged:te};Symbol.metadata??=Symbol("metadata"),ee.litPropertyMetadata??=new WeakMap;var S=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=Ce){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){let o=Symbol(),r=this.getPropertyDescriptor(e,o,t);r!==void 0&&it(this.prototype,e,r)}}static getPropertyDescriptor(e,t,o){let{get:r,set:n}=lt(this.prototype,e)??{get(){return this[t]},set(s){this[t]=s}};return{get:r,set(s){let l=r?.call(this);n?.call(this,s),this.requestUpdate(e,l,o)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??Ce}static _$Ei(){if(this.hasOwnProperty(q("elementProperties")))return;let e=pt(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(q("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(q("properties"))){let t=this.properties,o=[...ct(t),...dt(t)];for(let r of o)this.createProperty(r,t[r])}let e=this[Symbol.metadata];if(e!==null){let t=litPropertyMetadata.get(e);if(t!==void 0)for(let[o,r]of t)this.elementProperties.set(o,r)}this._$Eh=new Map;for(let[t,o]of this.elementProperties){let r=this._$Eu(t,o);r!==void 0&&this._$Eh.set(r,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){let t=[];if(Array.isArray(e)){let o=new Set(e.flat(1/0).reverse());for(let r of o)t.unshift(de(r))}else e!==void 0&&t.push(de(e));return t}static _$Eu(e,t){let o=t.attribute;return o===!1?void 0:typeof o=="string"?o:typeof e=="string"?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),this.renderRoot!==void 0&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){let e=new Map,t=this.constructor.elementProperties;for(let o of t.keys())this.hasOwnProperty(o)&&(e.set(o,this[o]),delete this[o]);e.size>0&&(this._$Ep=e)}createRenderRoot(){let e=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return $e(e,this.constructor.elementStyles),e}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,o){this._$AK(e,o)}_$ET(e,t){let o=this.constructor.elementProperties.get(e),r=this.constructor._$Eu(e,o);if(r!==void 0&&o.reflect===!0){let n=(o.converter?.toAttribute!==void 0?o.converter:I).toAttribute(t,o.type);this._$Em=e,n==null?this.removeAttribute(r):this.setAttribute(r,n),this._$Em=null}}_$AK(e,t){let o=this.constructor,r=o._$Eh.get(e);if(r!==void 0&&this._$Em!==r){let n=o.getPropertyOptions(r),s=typeof n.converter=="function"?{fromAttribute:n.converter}:n.converter?.fromAttribute!==void 0?n.converter:I;this._$Em=r;let l=s.fromAttribute(t,n.type);this[r]=l??this._$Ej?.get(r)??l,this._$Em=null}}requestUpdate(e,t,o,r=!1,n){if(e!==void 0){let s=this.constructor;if(r===!1&&(n=this[e]),o??=s.getPropertyOptions(e),!((o.hasChanged??te)(n,t)||o.useDefault&&o.reflect&&n===this._$Ej?.get(e)&&!this.hasAttribute(s._$Eu(e,o))))return;this.C(e,t,o)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(e,t,{useDefault:o,reflect:r,wrapped:n},s){o&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,s??t??this[e]),n!==!0||s!==void 0)||(this._$AL.has(e)||(this.hasUpdated||o||(t=void 0),this._$AL.set(e,t)),r===!0&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}let e=this.scheduleUpdate();return e!=null&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(let[r,n]of this._$Ep)this[r]=n;this._$Ep=void 0}let o=this.constructor.elementProperties;if(o.size>0)for(let[r,n]of o){let{wrapped:s}=n,l=this[r];s!==!0||this._$AL.has(r)||l===void 0||this.C(r,void 0,n,l)}}let e=!1,t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(o=>o.hostUpdate?.()),this.update(t)):this._$EM()}catch(o){throw e=!1,this._$EM(),o}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(e){}firstUpdated(e){}};S.elementStyles=[],S.shadowRootOptions={mode:"open"},S[q("elementProperties")]=new Map,S[q("finalized")]=new Map,_t?.({ReactiveElement:S}),(ee.reactiveElementVersions??=[]).push("2.1.2");var fe=globalThis,je=a=>a,oe=fe.trustedTypes,Ee=oe?oe.createPolicy("lit-html",{createHTML:a=>a}):void 0,ze="$lit$",A=`lit$${Math.random().toFixed(9).slice(2)}$`,Le="?"+A,ht=`<${Le}>`,E=document,B=()=>E.createComment(""),W=a=>a===null||typeof a!="object"&&typeof a!="function",be=Array.isArray,gt=a=>be(a)||typeof a?.[Symbol.iterator]=="function",pe=`[ -\f\r]`,F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Te=/-->/g,De=/>/g,C=RegExp(`>|${pe}(?:([^\\s"'>=/]+)(${pe}*=${pe}*(?:[^ -\f\r"'\`<>=]|("|')|))|$)`,"g"),Ne=/'/g,Re=/"/g,Oe=/^(?:script|style|textarea|title)$/i,ye=a=>(e,...t)=>({_$litType$:a,strings:e,values:t}),f=ye(1),Mt=ye(2),Ut=ye(3),T=Symbol.for("lit-noChange"),g=Symbol.for("lit-nothing"),Pe=new WeakMap,j=E.createTreeWalker(E,129);function Me(a,e){if(!be(a)||!a.hasOwnProperty("raw"))throw Error("invalid template strings array");return Ee!==void 0?Ee.createHTML(e):e}var mt=(a,e)=>{let t=a.length-1,o=[],r,n=e===2?"":e===3?"":"",s=F;for(let l=0;l"?(s=r??F,d=-1):u[1]===void 0?d=-2:(d=s.lastIndex-u[2].length,p=u[1],s=u[3]===void 0?C:u[3]==='"'?Re:Ne):s===Re||s===Ne?s=C:s===Te||s===De?s=F:(s=C,r=void 0);let h=s===C&&a[l+1].startsWith("/>")?" ":"";n+=s===F?c+ht:d>=0?(o.push(p),c.slice(0,d)+ze+c.slice(d)+A+h):c+A+(d===-2?l:h)}return[Me(a,n+(a[t]||"")+(e===2?"":e===3?"":"")),o]},Y=class a{constructor({strings:e,_$litType$:t},o){let r;this.parts=[];let n=0,s=0,l=e.length-1,c=this.parts,[p,u]=mt(e,t);if(this.el=a.createElement(p,o),j.currentNode=this.el.content,t===2||t===3){let d=this.el.content.firstChild;d.replaceWith(...d.childNodes)}for(;(r=j.nextNode())!==null&&c.length0){r.textContent=oe?oe.emptyScript:"";for(let h=0;h<_;h++)r.append(d[h],B()),j.nextNode(),c.push({type:2,index:++n});r.append(d[_],B())}}}else if(r.nodeType===8)if(r.data===Le)c.push({type:2,index:n});else{let d=-1;for(;(d=r.data.indexOf(A,d+1))!==-1;)c.push({type:7,index:n}),d+=A.length-1}n++}}static createElement(e,t){let o=E.createElement("template");return o.innerHTML=e,o}};function z(a,e,t=a,o){if(e===T)return e;let r=o!==void 0?t._$Co?.[o]:t._$Cl,n=W(e)?void 0:e._$litDirective$;return r?.constructor!==n&&(r?._$AO?.(!1),n===void 0?r=void 0:(r=new n(a),r._$AT(a,t,o)),o!==void 0?(t._$Co??=[])[o]=r:t._$Cl=r),r!==void 0&&(e=z(a,r._$AS(a,e.values),r,o)),e}var ue=class{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){let{el:{content:t},parts:o}=this._$AD,r=(e?.creationScope??E).importNode(t,!0);j.currentNode=r;let n=j.nextNode(),s=0,l=0,c=o[0];for(;c!==void 0;){if(s===c.index){let p;c.type===2?p=new G(n,n.nextSibling,this,e):c.type===1?p=new c.ctor(n,c.name,c.strings,this,e):c.type===6&&(p=new me(n,this,e)),this._$AV.push(p),c=o[++l]}s!==c?.index&&(n=j.nextNode(),s++)}return j.currentNode=E,r}p(e){let t=0;for(let o of this._$AV)o!==void 0&&(o.strings!==void 0?(o._$AI(e,o,t),t+=o.strings.length-2):o._$AI(e[t])),t++}},G=class a{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,o,r){this.type=2,this._$AH=g,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=o,this.options=r,this._$Cv=r?.isConnected??!0}get parentNode(){let e=this._$AA.parentNode,t=this._$AM;return t!==void 0&&e?.nodeType===11&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=z(this,e,t),W(e)?e===g||e==null||e===""?(this._$AH!==g&&this._$AR(),this._$AH=g):e!==this._$AH&&e!==T&&this._(e):e._$litType$!==void 0?this.$(e):e.nodeType!==void 0?this.T(e):gt(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==g&&W(this._$AH)?this._$AA.nextSibling.data=e:this.T(E.createTextNode(e)),this._$AH=e}$(e){let{values:t,_$litType$:o}=e,r=typeof o=="number"?this._$AC(e):(o.el===void 0&&(o.el=Y.createElement(Me(o.h,o.h[0]),this.options)),o);if(this._$AH?._$AD===r)this._$AH.p(t);else{let n=new ue(r,this),s=n.u(this.options);n.p(t),this.T(s),this._$AH=n}}_$AC(e){let t=Pe.get(e.strings);return t===void 0&&Pe.set(e.strings,t=new Y(e)),t}k(e){be(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,o,r=0;for(let n of e)r===t.length?t.push(o=new a(this.O(B()),this.O(B()),this,this.options)):o=t[r],o._$AI(n),r++;r2||o[0]!==""||o[1]!==""?(this._$AH=Array(o.length-1).fill(new String),this.strings=o):this._$AH=g}_$AI(e,t=this,o,r){let n=this.strings,s=!1;if(n===void 0)e=z(this,e,t,0),s=!W(e)||e!==this._$AH&&e!==T,s&&(this._$AH=e);else{let l=e,c,p;for(e=n[0],c=0;c{let o=t?.renderBefore??e,r=o._$litPart$;if(r===void 0){let n=t?.renderBefore??null;o._$litPart$=r=new G(e.insertBefore(B(),n),n,void 0,t??{})}return r._$AI(a),r};var ve=globalThis,$=class extends S{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){let e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=Ue(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return T}};$._$litElement$=!0,$.finalized=!0,ve.litElementHydrateSupport?.({LitElement:$});var bt=ve.litElementPolyfillSupport;bt?.({LitElement:$});(ve.litElementVersions??=[]).push("4.2.2");var yt={attribute:!0,type:String,converter:I,reflect:!1,hasChanged:te},vt=(a=yt,e,t)=>{let{kind:o,metadata:r}=t,n=globalThis.litPropertyMetadata.get(r);if(n===void 0&&globalThis.litPropertyMetadata.set(r,n=new Map),o==="setter"&&((a=Object.create(a)).wrapped=!0),n.set(t.name,a),o==="accessor"){let{name:s}=t;return{set(l){let c=e.get.call(this);e.set.call(this,l),this.requestUpdate(s,c,a,!0,l)},init(l){return l!==void 0&&this.C(s,void 0,a,l),l}}}if(o==="setter"){let{name:s}=t;return function(l){let c=this[s];e.call(this,l),this.requestUpdate(s,c,a,!0,l)}}throw Error("Unsupported decorator location: "+o)};function V(a){return(e,t)=>typeof t=="object"?vt(a,e,t):((o,r,n)=>{let s=r.hasOwnProperty(n);return r.constructor.createProperty(n,o),s?Object.getOwnPropertyDescriptor(r,n):void 0})(a,e,t)}function w(a){return V({...a,state:!0,attribute:!1})}var xt={days:1,weeks:7,months:30.4368,years:365.25};function He(a,e){return!a||a<=0?0:a*(xt[e||"days"]??1)}var qe=5;function K(a){let e=a.getFullYear(),t=String(a.getMonth()+1).padStart(2,"0"),o=String(a.getDate()).padStart(2,"0");return`${e}-${t}-${o}`}function wt(a,e){let t=[];for(let o=0;ot.cost).filter(t=>typeof t=="number");return e.length===0?null:e.reduce((t,o)=>t+o,0)/e.length}function St(a){let{windowStart:e,windowEnd:t,task:o,entryId:r,objectName:n}=a,s=[],l=(d,_)=>({date:d,entry_id:r,task_id:o.id,task_name:o.name,object_name:n,status:_&&(o.status==="overdue"||o.status==="triggered")?"ok":o.status,days_until_due:_?null:o.days_until_due??null,projected:_,schedule_type:o.schedule_type,interval_days:o.interval_days??null,interval_unit:o.interval_unit??null,responsible_user_id:o.responsible_user_id??null,avg_cost:kt(o.history),adaptive_enabled:!!o.adaptive_config?.enabled,prediction_confidence:o.threshold_prediction_confidence??null}),c=Math.max(1,Math.round(He(o.interval_days,o.interval_unit)));if(o.status==="overdue"||o.status==="triggered"){if(s.push(l(e,!1)),o.schedule_type==="time_based"&&o.interval_days&&o.interval_days>0){let d=ae(e,c),_=1;for(;d<=t&&_=e&&u<=t)s.push(l(u,!1));else if(u>t)return s;if(o.schedule_type==="time_based"&&o.interval_days&&o.interval_days>0){let d=ae(u,c),_=s.length;for(;d<=t&&_=e&&(s.push(l(d,!0)),_++),d=ae(d,c)}return s}var Ie={overdue:0,triggered:1,due_soon:2,ok:3};function Fe(a,e,t,o=null){let r=wt(e,t),n=r[0],s=r[r.length-1],l=[];for(let p of a){let u=p.object?.name||"",d=p.entry_id,_=p.tasks||[];for(let h of _){if(o&&h.responsible_user_id!==o||h.enabled===!1)continue;let b=St({windowStart:n,windowEnd:s,task:h,entryId:d,objectName:u});l.push(...b)}}let c=new Map;for(let p of r)c.set(p,[]);for(let p of l){let u=c.get(p.date);u&&u.push(p)}for(let[,p]of c)p.sort((u,d)=>{let _=Ie[u.status]??99,h=Ie[d.status]??99;if(_!==h)return _-h;if(u.projected!==d.projected)return u.projected?1:-1;let b=u.object_name.localeCompare(d.object_name);return b!==0?b:u.task_name.localeCompare(d.task_name)});return r.map(p=>({date:p,events:c.get(p)??[]}))}var $t={completed:"ok",reset:"ok",skipped:"due_soon",triggered:"triggered",trigger_replaced:"triggered",trigger_removed:"ok"};function At(a,e){let t=[];for(let o=e-1;o>=0;o--){let r=new Date(a);r.setDate(r.getDate()-o),r.setHours(0,0,0,0),t.push(K(r))}return t}function Be(a,e,t,o=null){let r=At(e,t),n=r[0],s=r[r.length-1],l=new Map;for(let p of r)l.set(p,[]);for(let p of a){let u=p.object?.name||"",d=p.entry_id,_=p.tasks||[];for(let h of _){if(o&&h.responsible_user_id!==o)continue;let b=h.history||[];for(let y of b){if(typeof y?.timestamp!="string")continue;let N=y.timestamp.slice(0,10);if(Ns)continue;let J=l.get(N);if(!J)continue;let i=y.type??"completed";J.push({date:N,entry_id:d,task_id:h.id,task_name:h.name,object_name:u,status:$t[i]??"ok",days_until_due:null,projected:!1,schedule_type:h.schedule_type,interval_days:h.interval_days??null,responsible_user_id:h.responsible_user_id??null,avg_cost:typeof y.cost=="number"?y.cost:null,adaptive_enabled:!!h.adaptive_config?.enabled,prediction_confidence:null,history_timestamp:y.timestamp,history_type:i,history_cost:typeof y.cost=="number"?y.cost:null,history_notes:typeof y.notes=="string"?y.notes:null,history_duration:typeof y.duration=="number"?y.duration:null})}}}let c={completed:0,reset:1,skipped:2,triggered:3,trigger_replaced:4};for(let[,p]of l)p.sort((u,d)=>{let _=c[u.history_type??""]??99,h=c[d.history_type??""]??99;if(_!==h)return _-h;let b=u.object_name.localeCompare(d.object_name);return b!==0?b:u.task_name.localeCompare(d.task_name)});return r.map(p=>({date:p,events:l.get(p)??[]}))}var We=k` +/*! maintenance_supporter frontend 2.46.0 */ +var it=Object.defineProperty;var lt=Object.getOwnPropertyDescriptor;var x=(a,e,t,o)=>{for(var r=o>1?void 0:o?lt(e,t):e,n=a.length-1,s;n>=0;n--)(s=a[n])&&(r=(o?s(e,t,r):s(r))||r);return o&&r&&it(e,t,r),r};var ee=globalThis,te=ee.ShadowRoot&&(ee.ShadyCSS===void 0||ee.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,de=Symbol(),Se=new WeakMap,I=class{constructor(e,t,o){if(this._$cssResult$=!0,o!==de)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e,this.t=t}get styleSheet(){let e=this.o,t=this.t;if(te&&e===void 0){let o=t!==void 0&&t.length===1;o&&(e=Se.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),o&&Se.set(t,e))}return e}toString(){return this.cssText}},$e=a=>new I(typeof a=="string"?a:a+"",void 0,de),k=(a,...e)=>{let t=a.length===1?a[0]:e.reduce((o,r,n)=>o+(s=>{if(s._$cssResult$===!0)return s.cssText;if(typeof s=="number")return s;throw Error("Value passed to 'css' function must be a 'css' function result: "+s+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(r)+a[n+1],a[0]);return new I(t,a,de)},Ae=(a,e)=>{if(te)a.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(let t of e){let o=document.createElement("style"),r=ee.litNonce;r!==void 0&&o.setAttribute("nonce",r),o.textContent=t.cssText,a.appendChild(o)}},pe=te?a=>a:a=>a instanceof CSSStyleSheet?(e=>{let t="";for(let o of e.cssRules)t+=o.cssText;return $e(t)})(a):a;var{is:ct,defineProperty:dt,getOwnPropertyDescriptor:pt,getOwnPropertyNames:ut,getOwnPropertySymbols:_t,getPrototypeOf:ht}=Object,oe=globalThis,je=oe.trustedTypes,gt=je?je.emptyScript:"",ft=oe.reactiveElementPolyfillSupport,F=(a,e)=>a,B={toAttribute(a,e){switch(e){case Boolean:a=a?gt:null;break;case Object:case Array:a=a==null?a:JSON.stringify(a)}return a},fromAttribute(a,e){let t=a;switch(e){case Boolean:t=a!==null;break;case Number:t=a===null?null:Number(a);break;case Object:case Array:try{t=JSON.parse(a)}catch{t=null}}return t}},re=(a,e)=>!ct(a,e),Ce={attribute:!0,type:String,converter:B,reflect:!1,useDefault:!1,hasChanged:re};Symbol.metadata??=Symbol("metadata"),oe.litPropertyMetadata??=new WeakMap;var S=class extends HTMLElement{static addInitializer(e){this._$Ei(),(this.l??=[]).push(e)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(e,t=Ce){if(t.state&&(t.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(e)&&((t=Object.create(t)).wrapped=!0),this.elementProperties.set(e,t),!t.noAccessor){let o=Symbol(),r=this.getPropertyDescriptor(e,o,t);r!==void 0&&dt(this.prototype,e,r)}}static getPropertyDescriptor(e,t,o){let{get:r,set:n}=pt(this.prototype,e)??{get(){return this[t]},set(s){this[t]=s}};return{get:r,set(s){let l=r?.call(this);n?.call(this,s),this.requestUpdate(e,l,o)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??Ce}static _$Ei(){if(this.hasOwnProperty(F("elementProperties")))return;let e=ht(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(F("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(F("properties"))){let t=this.properties,o=[...ut(t),..._t(t)];for(let r of o)this.createProperty(r,t[r])}let e=this[Symbol.metadata];if(e!==null){let t=litPropertyMetadata.get(e);if(t!==void 0)for(let[o,r]of t)this.elementProperties.set(o,r)}this._$Eh=new Map;for(let[t,o]of this.elementProperties){let r=this._$Eu(t,o);r!==void 0&&this._$Eh.set(r,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(e){let t=[];if(Array.isArray(e)){let o=new Set(e.flat(1/0).reverse());for(let r of o)t.unshift(pe(r))}else e!==void 0&&t.push(pe(e));return t}static _$Eu(e,t){let o=t.attribute;return o===!1?void 0:typeof o=="string"?o:typeof e=="string"?e.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(e=>this.enableUpdating=e),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(e=>e(this))}addController(e){(this._$EO??=new Set).add(e),this.renderRoot!==void 0&&this.isConnected&&e.hostConnected?.()}removeController(e){this._$EO?.delete(e)}_$E_(){let e=new Map,t=this.constructor.elementProperties;for(let o of t.keys())this.hasOwnProperty(o)&&(e.set(o,this[o]),delete this[o]);e.size>0&&(this._$Ep=e)}createRenderRoot(){let e=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return Ae(e,this.constructor.elementStyles),e}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(e=>e.hostConnected?.())}enableUpdating(e){}disconnectedCallback(){this._$EO?.forEach(e=>e.hostDisconnected?.())}attributeChangedCallback(e,t,o){this._$AK(e,o)}_$ET(e,t){let o=this.constructor.elementProperties.get(e),r=this.constructor._$Eu(e,o);if(r!==void 0&&o.reflect===!0){let n=(o.converter?.toAttribute!==void 0?o.converter:B).toAttribute(t,o.type);this._$Em=e,n==null?this.removeAttribute(r):this.setAttribute(r,n),this._$Em=null}}_$AK(e,t){let o=this.constructor,r=o._$Eh.get(e);if(r!==void 0&&this._$Em!==r){let n=o.getPropertyOptions(r),s=typeof n.converter=="function"?{fromAttribute:n.converter}:n.converter?.fromAttribute!==void 0?n.converter:B;this._$Em=r;let l=s.fromAttribute(t,n.type);this[r]=l??this._$Ej?.get(r)??l,this._$Em=null}}requestUpdate(e,t,o,r=!1,n){if(e!==void 0){let s=this.constructor;if(r===!1&&(n=this[e]),o??=s.getPropertyOptions(e),!((o.hasChanged??re)(n,t)||o.useDefault&&o.reflect&&n===this._$Ej?.get(e)&&!this.hasAttribute(s._$Eu(e,o))))return;this.C(e,t,o)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(e,t,{useDefault:o,reflect:r,wrapped:n},s){o&&!(this._$Ej??=new Map).has(e)&&(this._$Ej.set(e,s??t??this[e]),n!==!0||s!==void 0)||(this._$AL.has(e)||(this.hasUpdated||o||(t=void 0),this._$AL.set(e,t)),r===!0&&this._$Em!==e&&(this._$Eq??=new Set).add(e))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}let e=this.scheduleUpdate();return e!=null&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(let[r,n]of this._$Ep)this[r]=n;this._$Ep=void 0}let o=this.constructor.elementProperties;if(o.size>0)for(let[r,n]of o){let{wrapped:s}=n,l=this[r];s!==!0||this._$AL.has(r)||l===void 0||this.C(r,void 0,n,l)}}let e=!1,t=this._$AL;try{e=this.shouldUpdate(t),e?(this.willUpdate(t),this._$EO?.forEach(o=>o.hostUpdate?.()),this.update(t)):this._$EM()}catch(o){throw e=!1,this._$EM(),o}e&&this._$AE(t)}willUpdate(e){}_$AE(e){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(e){return!0}update(e){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(e){}firstUpdated(e){}};S.elementStyles=[],S.shadowRootOptions={mode:"open"},S[F("elementProperties")]=new Map,S[F("finalized")]=new Map,ft?.({ReactiveElement:S}),(oe.reactiveElementVersions??=[]).push("2.1.2");var be=globalThis,Ee=a=>a,ae=be.trustedTypes,Te=ae?ae.createPolicy("lit-html",{createHTML:a=>a}):void 0,Oe="$lit$",A=`lit$${Math.random().toFixed(9).slice(2)}$`,Le="?"+A,mt=`<${Le}>`,T=document,Y=()=>T.createComment(""),G=a=>a===null||typeof a!="object"&&typeof a!="function",ye=Array.isArray,bt=a=>ye(a)||typeof a?.[Symbol.iterator]=="function",ue=`[ +\f\r]`,W=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,De=/-->/g,Ne=/>/g,C=RegExp(`>|${ue}(?:([^\\s"'>=/]+)(${ue}*=${ue}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),Re=/'/g,Pe=/"/g,Me=/^(?:script|style|textarea|title)$/i,ve=a=>(e,...t)=>({_$litType$:a,strings:e,values:t}),m=ve(1),qt=ve(2),It=ve(3),D=Symbol.for("lit-noChange"),g=Symbol.for("lit-nothing"),ze=new WeakMap,E=T.createTreeWalker(T,129);function Ue(a,e){if(!ye(a)||!a.hasOwnProperty("raw"))throw Error("invalid template strings array");return Te!==void 0?Te.createHTML(e):e}var yt=(a,e)=>{let t=a.length-1,o=[],r,n=e===2?"":e===3?"":"",s=W;for(let l=0;l"?(s=r??W,c=-1):u[1]===void 0?c=-2:(c=s.lastIndex-u[2].length,p=u[1],s=u[3]===void 0?C:u[3]==='"'?Pe:Re):s===Pe||s===Re?s=C:s===De||s===Ne?s=W:(s=C,r=void 0);let h=s===C&&a[l+1].startsWith("/>")?" ":"";n+=s===W?d+mt:c>=0?(o.push(p),d.slice(0,c)+Oe+d.slice(c)+A+h):d+A+(c===-2?l:h)}return[Ue(a,n+(a[t]||"")+(e===2?"":e===3?"":"")),o]},V=class a{constructor({strings:e,_$litType$:t},o){let r;this.parts=[];let n=0,s=0,l=e.length-1,d=this.parts,[p,u]=yt(e,t);if(this.el=a.createElement(p,o),E.currentNode=this.el.content,t===2||t===3){let c=this.el.content.firstChild;c.replaceWith(...c.childNodes)}for(;(r=E.nextNode())!==null&&d.length0){r.textContent=ae?ae.emptyScript:"";for(let h=0;h<_;h++)r.append(c[h],Y()),E.nextNode(),d.push({type:2,index:++n});r.append(c[_],Y())}}}else if(r.nodeType===8)if(r.data===Le)d.push({type:2,index:n});else{let c=-1;for(;(c=r.data.indexOf(A,c+1))!==-1;)d.push({type:7,index:n}),c+=A.length-1}n++}}static createElement(e,t){let o=T.createElement("template");return o.innerHTML=e,o}};function z(a,e,t=a,o){if(e===D)return e;let r=o!==void 0?t._$Co?.[o]:t._$Cl,n=G(e)?void 0:e._$litDirective$;return r?.constructor!==n&&(r?._$AO?.(!1),n===void 0?r=void 0:(r=new n(a),r._$AT(a,t,o)),o!==void 0?(t._$Co??=[])[o]=r:t._$Cl=r),r!==void 0&&(e=z(a,r._$AS(a,e.values),r,o)),e}var _e=class{constructor(e,t){this._$AV=[],this._$AN=void 0,this._$AD=e,this._$AM=t}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(e){let{el:{content:t},parts:o}=this._$AD,r=(e?.creationScope??T).importNode(t,!0);E.currentNode=r;let n=E.nextNode(),s=0,l=0,d=o[0];for(;d!==void 0;){if(s===d.index){let p;d.type===2?p=new K(n,n.nextSibling,this,e):d.type===1?p=new d.ctor(n,d.name,d.strings,this,e):d.type===6&&(p=new me(n,this,e)),this._$AV.push(p),d=o[++l]}s!==d?.index&&(n=E.nextNode(),s++)}return E.currentNode=T,r}p(e){let t=0;for(let o of this._$AV)o!==void 0&&(o.strings!==void 0?(o._$AI(e,o,t),t+=o.strings.length-2):o._$AI(e[t])),t++}},K=class a{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,o,r){this.type=2,this._$AH=g,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=o,this.options=r,this._$Cv=r?.isConnected??!0}get parentNode(){let e=this._$AA.parentNode,t=this._$AM;return t!==void 0&&e?.nodeType===11&&(e=t.parentNode),e}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(e,t=this){e=z(this,e,t),G(e)?e===g||e==null||e===""?(this._$AH!==g&&this._$AR(),this._$AH=g):e!==this._$AH&&e!==D&&this._(e):e._$litType$!==void 0?this.$(e):e.nodeType!==void 0?this.T(e):bt(e)?this.k(e):this._(e)}O(e){return this._$AA.parentNode.insertBefore(e,this._$AB)}T(e){this._$AH!==e&&(this._$AR(),this._$AH=this.O(e))}_(e){this._$AH!==g&&G(this._$AH)?this._$AA.nextSibling.data=e:this.T(T.createTextNode(e)),this._$AH=e}$(e){let{values:t,_$litType$:o}=e,r=typeof o=="number"?this._$AC(e):(o.el===void 0&&(o.el=V.createElement(Ue(o.h,o.h[0]),this.options)),o);if(this._$AH?._$AD===r)this._$AH.p(t);else{let n=new _e(r,this),s=n.u(this.options);n.p(t),this.T(s),this._$AH=n}}_$AC(e){let t=ze.get(e.strings);return t===void 0&&ze.set(e.strings,t=new V(e)),t}k(e){ye(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,o,r=0;for(let n of e)r===t.length?t.push(o=new a(this.O(Y()),this.O(Y()),this,this.options)):o=t[r],o._$AI(n),r++;r2||o[0]!==""||o[1]!==""?(this._$AH=Array(o.length-1).fill(new String),this.strings=o):this._$AH=g}_$AI(e,t=this,o,r){let n=this.strings,s=!1;if(n===void 0)e=z(this,e,t,0),s=!G(e)||e!==this._$AH&&e!==D,s&&(this._$AH=e);else{let l=e,d,p;for(e=n[0],d=0;d{let o=t?.renderBefore??e,r=o._$litPart$;if(r===void 0){let n=t?.renderBefore??null;o._$litPart$=r=new K(e.insertBefore(Y(),n),n,void 0,t??{})}return r._$AI(a),r};var xe=globalThis,$=class extends S{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){let e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){let t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=He(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return D}};$._$litElement$=!0,$.finalized=!0,xe.litElementHydrateSupport?.({LitElement:$});var xt=xe.litElementPolyfillSupport;xt?.({LitElement:$});(xe.litElementVersions??=[]).push("4.2.2");var wt={attribute:!0,type:String,converter:B,reflect:!1,hasChanged:re},kt=(a=wt,e,t)=>{let{kind:o,metadata:r}=t,n=globalThis.litPropertyMetadata.get(r);if(n===void 0&&globalThis.litPropertyMetadata.set(r,n=new Map),o==="setter"&&((a=Object.create(a)).wrapped=!0),n.set(t.name,a),o==="accessor"){let{name:s}=t;return{set(l){let d=e.get.call(this);e.set.call(this,l),this.requestUpdate(s,d,a,!0,l)},init(l){return l!==void 0&&this.C(s,void 0,a,l),l}}}if(o==="setter"){let{name:s}=t;return function(l){let d=this[s];e.call(this,l),this.requestUpdate(s,d,a,!0,l)}}throw Error("Unsupported decorator location: "+o)};function Q(a){return(e,t)=>typeof t=="object"?kt(a,e,t):((o,r,n)=>{let s=r.hasOwnProperty(n);return r.constructor.createProperty(n,o),s?Object.getOwnPropertyDescriptor(r,n):void 0})(a,e,t)}function w(a){return Q({...a,state:!0,attribute:!1})}var St={days:1,weeks:7,months:30.4368,years:365.25};function qe(a,e){return!a||a<=0?0:a*(St[e||"days"]??1)}var Ie=5;function J(a){let e=a.getFullYear(),t=String(a.getMonth()+1).padStart(2,"0"),o=String(a.getDate()).padStart(2,"0");return`${e}-${t}-${o}`}function $t(a,e){let t=[];for(let o=0;ot.cost).filter(t=>typeof t=="number");return e.length===0?null:e.reduce((t,o)=>t+o,0)/e.length}function jt(a){let{windowStart:e,windowEnd:t,task:o,entryId:r,objectName:n}=a,s=[],l=(c,_)=>({date:c,entry_id:r,task_id:o.id,task_name:o.name,object_name:n,status:_&&(o.status==="overdue"||o.status==="triggered")?"ok":o.status,days_until_due:_?null:o.days_until_due??null,projected:_,schedule_type:o.schedule_type,interval_days:o.interval_days??null,interval_unit:o.interval_unit??null,responsible_user_id:o.responsible_user_id??null,avg_cost:At(o.history),adaptive_enabled:!!o.adaptive_config?.enabled,prediction_confidence:o.threshold_prediction_confidence??null}),d=Math.max(1,Math.round(qe(o.interval_days,o.interval_unit)));if(o.status==="overdue"||o.status==="triggered"){if(s.push(l(e,!1)),o.schedule_type==="time_based"&&o.interval_days&&o.interval_days>0){let c=se(e,d),_=1;for(;c<=t&&_=e&&u<=t)s.push(l(u,!1));else if(u>t)return s;if(o.schedule_type==="time_based"&&o.interval_days&&o.interval_days>0){let c=se(u,d),_=s.length;for(;c<=t&&_=e&&(s.push(l(c,!0)),_++),c=se(c,d)}return s}var Fe={overdue:0,triggered:1,due_soon:2,ok:3};function Be(a,e,t,o=null){let r=$t(e,t),n=r[0],s=r[r.length-1],l=[];for(let p of a){let u=p.object?.name||"",c=p.entry_id,_=p.tasks||[];for(let h of _){if(o&&h.responsible_user_id!==o||h.enabled===!1)continue;let b=jt({windowStart:n,windowEnd:s,task:h,entryId:c,objectName:u});l.push(...b)}}let d=new Map;for(let p of r)d.set(p,[]);for(let p of l){let u=d.get(p.date);u&&u.push(p)}for(let[,p]of d)p.sort((u,c)=>{let _=Fe[u.status]??99,h=Fe[c.status]??99;if(_!==h)return _-h;if(u.projected!==c.projected)return u.projected?1:-1;let b=u.object_name.localeCompare(c.object_name);return b!==0?b:u.task_name.localeCompare(c.task_name)});return r.map(p=>({date:p,events:d.get(p)??[]}))}var Ct={completed:"ok",reset:"ok",skipped:"due_soon",triggered:"triggered",trigger_replaced:"triggered",trigger_removed:"ok"};function Et(a,e){let t=[];for(let o=e-1;o>=0;o--){let r=new Date(a);r.setDate(r.getDate()-o),r.setHours(0,0,0,0),t.push(J(r))}return t}function We(a,e,t,o=null){let r=Et(e,t),n=r[0],s=r[r.length-1],l=new Map;for(let p of r)l.set(p,[]);for(let p of a){let u=p.object?.name||"",c=p.entry_id,_=p.tasks||[];for(let h of _){if(o&&h.responsible_user_id!==o)continue;let b=h.history||[];for(let y of b){if(typeof y?.timestamp!="string")continue;let R=y.timestamp.slice(0,10);if(Rs)continue;let M=l.get(R);if(!M)continue;let U=y.type??"completed";M.push({date:R,entry_id:c,task_id:h.id,task_name:h.name,object_name:u,status:Ct[U]??"ok",days_until_due:null,projected:!1,schedule_type:h.schedule_type,interval_days:h.interval_days??null,responsible_user_id:h.responsible_user_id??null,avg_cost:typeof y.cost=="number"?y.cost:null,adaptive_enabled:!!h.adaptive_config?.enabled,prediction_confidence:null,history_timestamp:y.timestamp,history_type:U,history_cost:typeof y.cost=="number"?y.cost:null,history_notes:typeof y.notes=="string"?y.notes:null,history_duration:typeof y.duration=="number"?y.duration:null})}}}let d={completed:0,reset:1,skipped:2,triggered:3,trigger_replaced:4};for(let[,p]of l)p.sort((u,c)=>{let _=d[u.history_type??""]??99,h=d[c.history_type??""]??99;if(_!==h)return _-h;let b=u.object_name.localeCompare(c.object_name);return b!==0?b:u.task_name.localeCompare(c.task_name)});return r.map(p=>({date:p,events:l.get(p)??[]}))}var Ye=k` .cal-controls { display: flex; gap: 12px; @@ -198,9 +198,9 @@ var at=Object.defineProperty;var nt=Object.getOwnPropertyDescriptor;var x=(a,e,t .cal-pill-day { font-size: 17px; } .cal-user-filter { margin-left: 0; width: 100%; } } -`;var Ye={maintenance:"Maintenance",objects:"Objects",tasks:"Tasks",overdue:"Overdue",due_soon:"Due Soon",triggered:"Triggered",trigger_replaced:"Trigger replaced",ok:"OK",all:"All",new_object:"+ New Object",templates_from:"From template",templates_title:"Start from a template",templates_task_count:"{n} tasks",template_created:"Created from template",onboard_hint:"Add your first object to start tracking maintenance.",edit:"Edit",duplicate:"Duplicate",task_duplicated:"Task duplicated",object_duplicated:"Object duplicated",delete:"Delete",add_task:"+ Add Task",complete:"Complete",completed:"Completed",skip:"Skip",skipped:"Skipped",missed:"Missed",reset:"Reset",snooze:"Snooze",snoozed:"Snoozed",cancel:"Cancel",bulk_select:"Select",bulk_select_all:"Select all",bulk_n_selected:"{n} selected",bulk_completed:"{n} tasks completed",bulk_archived:"{n} tasks archived",completing:"Completing\u2026",interval:"Interval",warning:"Warning",last_performed:"Last performed",next_due:"Next due",days_until_due:"Days until due",avg_duration:"Avg duration",trigger:"Trigger",trigger_type:"Trigger type",threshold_above:"Upper limit",threshold_below:"Lower limit",threshold:"Threshold",counter:"Counter",state_change:"State change",runtime:"Runtime",runtime_hours:"Target runtime (hours)",target_value:"Target value",baseline:"Baseline",target_changes:"Target changes",for_minutes:"For (minutes)",time_based:"Time-based",sensor_based:"Sensor-based",manual:"Manual",one_time:"One-time",weekdays:"Weekdays",nth_weekday:"Nth weekday of month",day_of_month:"Day of month",recurrence_on_days:"Repeat on",recurrence_occurrence:"Occurrence",recurrence_weekday:"Weekday",recurrence_day:"Day of month (1\u201331)",recurrence_last_day:"Last day of the month",recurrence_business_day:"Business days only (roll back from weekend)",recurrence_offset:"Offset (days, \xB1)",recurrence_offset_help:"Shift the date by \xB1N days, e.g. -2 = two days before.",last_day_month:"Last day of month",last_business_day_month:"Last business day",ord_1:"1st",ord_2:"2nd",ord_3:"3rd",ord_4:"4th",ord_5:"5th",ord_last:"Last",day_word:"Day",interval_value:"Interval",interval_unit:"Unit",unit_days:"Days",unit_weeks:"Weeks",unit_months:"Months",unit_years:"Years",due_date:"Due date",cleaning:"Cleaning",inspection:"Inspection",replacement:"Replacement",calibration:"Calibration",service:"Service",reading:"Reading",custom:"Custom",history:"History",cost:"Cost",report_button:"Report",report_title:"Maintenance report",report_generated:"Generated",report_times_done:"Done",report_total_cost:"Total cost",report_every:"every {n} {unit}",report_notes:"Notes",report_col_type:"Type",report_col_status:"Status",report_col_schedule:"Schedule",duration:"Duration",both:"Both",trigger_val:"Trigger value",complete_title:"Complete: ",checklist:"Checklist",require_on_completion:"Require on completion",checklist_steps_optional:"Checklist steps (optional)",checklist_placeholder:`Clean filter +`;var Ge={maintenance:"Maintenance",objects:"Objects",tasks:"Tasks",overdue:"Overdue",due_soon:"Due Soon",triggered:"Triggered",trigger_replaced:"Trigger replaced",ok:"OK",all:"All",new_object:"+ New Object",templates_from:"From template",templates_title:"Start from a template",templates_task_count:"{n} tasks",template_created:"Created from template",onboard_hint:"Add your first object to start tracking maintenance.",edit:"Edit",duplicate:"Duplicate",task_duplicated:"Task duplicated",object_duplicated:"Object duplicated",delete:"Delete",add_task:"+ Add Task",complete:"Complete",completed:"Completed",skip:"Skip",skipped:"Skipped",missed:"Missed",reset:"Reset",snooze:"Snooze",snoozed:"Snoozed",cancel:"Cancel",bulk_select:"Select",bulk_select_all:"Select all",bulk_n_selected:"{n} selected",bulk_completed:"{n} tasks completed",bulk_archived:"{n} tasks archived",completing:"Completing\u2026",interval:"Interval",warning:"Warning",last_performed:"Last performed",next_due:"Next due",days_until_due:"Days until due",avg_duration:"Avg duration",trigger:"Trigger",trigger_type:"Trigger type",threshold_above:"Upper limit",threshold_below:"Lower limit",threshold:"Threshold",counter:"Counter",state_change:"State change",runtime:"Runtime",runtime_hours:"Target runtime (hours)",target_value:"Target value",baseline:"Baseline",target_changes:"Target changes",for_minutes:"For (minutes)",time_based:"Time-based",sensor_based:"Sensor-based",manual:"Manual",one_time:"One-time",weekdays:"Weekdays",nth_weekday:"Nth weekday of month",day_of_month:"Day of month",recurrence_on_days:"Repeat on",recurrence_occurrence:"Occurrence",recurrence_weekday:"Weekday",recurrence_day:"Day of month (1\u201331)",recurrence_last_day:"Last day of the month",recurrence_business_day:"Business days only (roll back from weekend)",recurrence_offset:"Offset (days, \xB1)",recurrence_offset_help:"Shift the date by \xB1N days, e.g. -2 = two days before.",last_day_month:"Last day of month",last_business_day_month:"Last business day",ord_1:"1st",ord_2:"2nd",ord_3:"3rd",ord_4:"4th",ord_5:"5th",ord_last:"Last",day_word:"Day",interval_value:"Interval",interval_unit:"Unit",unit_days:"Days",unit_weeks:"Weeks",unit_months:"Months",unit_years:"Years",due_date:"Due date",cleaning:"Cleaning",inspection:"Inspection",replacement:"Replacement",calibration:"Calibration",service:"Service",reading:"Reading",custom:"Custom",history:"History",cost:"Cost",report_button:"Report",report_title:"Maintenance report",report_generated:"Generated",report_times_done:"Done",report_total_cost:"Total cost",report_every:"every {n} {unit}",report_notes:"Notes",report_col_type:"Type",report_col_status:"Status",report_col_schedule:"Schedule",duration:"Duration",both:"Both",trigger_val:"Trigger value",complete_title:"Complete: ",checklist:"Checklist",require_on_completion:"Require on completion",checklist_steps_optional:"Checklist steps (optional)",checklist_placeholder:`Clean filter Replace seal -Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"{field}: too long (max {n} characters)",err_too_short:"{field}: too short (min {n} characters)",err_value_too_high:"{field}: too large (max {n})",err_value_too_low:"{field}: too small (min {n})",err_required:"{field}: required",err_wrong_type:"{field}: wrong type (expected: {type})",err_invalid_choice:"{field}: not an allowed value",err_invalid_value:"{field}: invalid value",feat_schedule_time:"Time-of-day scheduling",feat_schedule_time_desc:"Tasks become overdue at a specific time of day instead of midnight.",schedule_time_optional:"Due at time (optional, HH:MM)",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",days:"days",day:"day",today:"Today",d_overdue:"d overdue",no_tasks:"No maintenance tasks yet. Create an object to get started.",no_tasks_short:"No tasks",no_history:"No history entries yet.",show_all:"Show all",cost_duration_chart:"Cost & Duration",installed:"Installed",confirm_delete_object:"Delete this object and all its tasks?",confirm_delete_task:"Delete this task?",min:"Min",max:"Max",save:"Save",saving:"Saving\u2026",edit_task:"Edit Task",new_task:"New Maintenance Task",task_name:"Task name",maintenance_type:"Maintenance type",priority:"Priority",labels:"Labels",labels_placeholder:"e.g. safety, seasonal, tenant-visible",labels_help:"Comma-separated tags for filtering and reporting.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",schedule_type:"Schedule type",interval_days:"Interval (days)",warning_days:"Warning days",earliest_completion_days:"Earliest completion (days before due)",earliest_completion_days_help:"Leave empty to allow completing any time. 0 = only on/after the due date.",last_performed_optional:"Last performed (optional)",interval_anchor:"Interval anchor",anchor_completion:"From completion date",anchor_planned:"From planned date (no drift)",edit_object:"Edit Object",name:"Name",manufacturer_optional:"Manufacturer (optional)",model_optional:"Model (optional)",serial_number_optional:"Serial number (optional)",serial_number_label:"S/N",documentation_url_label:"Manual",object_notes_label:"Notes",sort_due_date:"Due date",sort_object:"Object name",sort_type:"Type",sort_task_name:"Task name",all_objects:"All objects",tasks_lower:"tasks",no_tasks_yet:"No tasks yet",add_first_task:"Add first task",trigger_configuration:"Trigger Configuration",entity_id:"Entity ID",comma_separated:"comma-separated",entity_logic:"Entity logic",entity_logic_any:"Any entity triggers",entity_logic_all:"All entities must trigger",entities:"entities",attribute_optional:"Attribute (optional, blank = state)",use_entity_state:"Use entity state (no attribute)",trigger_above:"Trigger above",trigger_below:"Trigger below",for_at_least_minutes:"For at least (minutes)",safety_interval_days:"Safety interval (days, optional)",safety_interval:"Safety interval (optional)",delta_mode:"Delta mode",from_state_optional:"From state (optional)",to_state_optional:"To state (optional)",documentation_url_optional:"Documentation URL (optional)",object_notes_optional:"Notes (optional)",nfc_tag_id_optional:"NFC Tag ID (optional)",nfc_tags_empty_help:"No NFC tags registered in Home Assistant yet.",nfc_tags_open_settings:"Open Tags settings",nfc_tags_refresh:"Refresh",environmental_entity_optional:"Environmental sensor (optional)",environmental_entity_helper:"e.g. sensor.outdoor_temperature \u2014 adjusts the interval based on environmental conditions",environmental_attribute_optional:"Environmental attribute (optional)",nfc_tag_id:"NFC Tag ID",nfc_linked:"NFC tag linked",nfc_link_hint:"Click to link NFC tag",responsible_user:"Responsible User",shared_with:"Shared with (rotation)",shared_with_help:"Pick multiple people to share this task; the responsible person rotates on each completion.",rotation_strategy:"Rotation",rotation_none:"No rotation",rotation_round_robin:"Round-robin",rotation_least_completed:"Least completed",rotation_random:"Random",no_user_assigned:"(No user assigned)",all_users:"All Users",my_tasks:"My Tasks",tab_calendar:"Calendar",cal_no_events:"No maintenance",cal_window_7:"7 days",cal_window_14:"14 days",cal_window_30:"30 days",cal_window_365:"1 year",cal_every_n_days:"every {n} days",cal_source_time:"Time-based",cal_source_time_adaptive:"Time-based (adaptive)",cal_source_sensor:"Sensor-based",cal_predicted:"predicted",cal_confidence_high:"high confidence",cal_confidence_medium:"medium confidence",cal_confidence_low:"low confidence",budget_monthly:"Monthly budget",budget_yearly:"Yearly budget",groups:"Groups",new_group:"New group",edit_group:"Edit group",no_groups:"No groups yet",delete_group:"Delete group",delete_group_confirm:"Delete group '{name}'?",group_select_tasks:"Select tasks",group_name_required:"Name is required",description_optional:"Description (optional)",selected:"Selected",loading_chart:"Loading chart data...",hide_outliers:"Hide outliers (sensor glitches)",was_maintenance_needed:"Was this maintenance needed?",feedback_needed:"Needed",feedback_not_needed:"Not needed",feedback_not_sure:"Not sure",suggested_interval:"Suggested interval",apply_suggestion:"Apply",reanalyze:"Re-analyze",reanalyze_result:"New analysis",reanalyze_insufficient_data:"Not enough data to produce a recommendation",data_points:"data points",dismiss_suggestion:"Dismiss",confidence_low:"Low",confidence_medium:"Medium",confidence_high:"High",recommended:"recommended",seasonal_awareness:"Seasonal Awareness",edit_seasonal_overrides:"Edit seasonal factors",seasonal_overrides_title:"Seasonal factors (override)",seasonal_overrides_hint:"Factor per month (0.1\u20135.0). Empty = learned automatically.",seasonal_override_invalid:"Invalid value",seasonal_override_range:"Factor must be between 0.1 and 5.0",clear_all:"Clear all",seasonal_chart_title:"Seasonal Factors",seasonal_learned:"Learned",seasonal_manual:"Manual",month_jan:"Jan",month_feb:"Feb",month_mar:"Mar",month_apr:"Apr",month_may:"May",month_jun:"Jun",month_jul:"Jul",month_aug:"Aug",month_sep:"Sep",month_oct:"Oct",month_nov:"Nov",month_dec:"Dec",sensor_prediction:"Sensor Prediction",degradation_trend:"Trend",trend_rising:"Rising",trend_falling:"Falling",trend_stable:"Stable",trend_insufficient_data:"Insufficient data",days_until_threshold:"Days until threshold",threshold_exceeded:"Threshold exceeded",environmental_adjustment:"Environmental factor",sensor_prediction_urgency:"Sensor predicts threshold in ~{days} days",day_short:"day",weibull_reliability_curve:"Reliability Curve",weibull_failure_probability:"Failure Probability",weibull_r_squared:"Fit R\xB2",beta_early_failures:"Early Failures",beta_random_failures:"Random Failures",beta_wear_out:"Wear-out",beta_highly_predictable:"Highly Predictable",confidence_interval:"Confidence Interval",confidence_conservative:"Conservative",confidence_aggressive:"Optimistic",current_interval_marker:"Current interval",recommended_marker:"Recommended",characteristic_life:"Characteristic life",chart_mini_sparkline:"Trend sparkline",chart_history:"Cost and duration history",chart_seasonal:"Seasonal factors, 12 months",chart_weibull:"Weibull reliability curve",chart_sparkline:"Sensor trigger value chart",days_progress:"Days progress",qr_code:"QR Code",qr_generating:"Generating QR code\u2026",qr_error:"Failed to generate QR code.",qr_error_no_url:"No HA URL configured. Please set an external or internal URL in Settings \u2192 System \u2192 Network.",save_error:"Failed to save. Please try again.",qr_print:"Print",qr_download:"Download SVG",qr_action:"Action on scan",qr_action_view:"View maintenance info",qr_action_complete:"Mark maintenance as complete",qr_url_mode:"Link type",qr_mode_companion:"Companion App",qr_mode_local:"Local (mDNS)",qr_mode_server:"Server URL",overview:"Overview",analysis:"Analysis",recent_activities:"Recent Activities",search_notes:"Search notes",avg_cost:"Avg Cost",no_advanced_features:"No advanced features enabled",no_advanced_features_hint:"Enable \u201CAdaptive Intervals\u201D or \u201CSeasonal Patterns\u201D in the integration settings to see analysis data here.",analysis_not_enough_data:"Not enough data for analysis yet.",analysis_not_enough_data_hint:"Weibull analysis requires at least 5 completed maintenances; seasonal patterns become visible after 6+ data points per month.",analysis_manual_task_hint:"Manual tasks without an interval do not generate analysis data.",completions:"completions",current:"Current",shorter:"Shorter",longer:"Longer",normal:"Normal",disabled:"Disabled",compound_logic:"Compound logic",compound:"Compound (multiple conditions)",compound_logic_and:"AND \u2014 all conditions must trigger",compound_logic_or:"OR \u2014 any condition triggers",compound_help:"Combine several sensor conditions into one trigger.",compound_no_conditions:"No conditions yet \u2014 add at least one.",compound_add_condition:"Add condition",compound_condition:"Condition",compound_remove_condition:"Remove condition",card_title:"Title",card_show_header:"Show header with statistics",card_show_actions:"Show action buttons",card_compact:"Compact mode",card_max_items:"Max items (0 = all)",card_filter_status:"Filter by status",card_filter_status_help:"Empty = show all statuses.",card_filter_objects:"Filter by objects",card_filter_objects_help:"Empty = show all objects.",card_filter_areas:"Filter by areas",card_filter_areas_help:"Empty = show all areas.",card_filter_entities:"Filter by entities (entity_ids)",card_filter_entities_help:"Pick sensor / binary_sensor entities from this integration. Empty = all.",card_loading_objects:"Loading objects\u2026",card_load_error:"Could not load objects \u2014 check the WebSocket connection.",card_no_tasks_title:"No maintenance tasks yet",card_no_tasks_cta:"\u2192 Create one in the Maintenance panel",no_objects:"No objects yet.",action_error:"Action failed. Please try again.",area_id_optional:"Area (optional)",installation_date_optional:"Installation date (optional)",warranty_expiry_optional:"Warranty expiry (optional)",warranty:"Warranty",warranty_valid_until:"valid until {date}",warranty_expires_in:"expires in {days} days",warranty_expired:"expired",cal_past_windows:"Past windows",cal_forward_windows:"Forward windows",history_edit_title:"Edit history entry",history_edit_timestamp:"Timestamp",manufacturer:"Manufacturer",model:"Model",area:"Area",actions:"Actions",view_mode_label:"View",view_cards:"Card view",view_table:"Table view",objects_table_columns_label:"Objects table columns",objects_table_columns_hint:"Choose which columns appear in the objects table view.",custom_icon_optional:"Icon (optional, e.g. mdi:wrench)",task_enabled:"Task enabled",skip_reason_prompt:"Skip this task?",reason_optional:"Reason (optional)",reset_date_prompt:"Mark task as performed?",reset_date_optional:"Last performed date (optional, defaults to today)",notes_label:"Notes",documentation_label:"Documentation",no_nfc_tag:"\u2014 No tag \u2014",dashboard:"Dashboard",tab_today:"Today",palette_placeholder:"Search objects and tasks\u2026",palette_no_results:"No matches",palette_hint:"\u2191\u2193 to navigate \xB7 Enter to open \xB7 Esc to close",today_all_caught_up:"All caught up! Nothing due this week.",today_overdue:"Overdue",today_due_today:"Due today",today_this_week:"This week",settings:"Settings",settings_features:"Advanced Features",settings_features_desc:"Enable or disable advanced features. Disabling hides them from the UI but does not delete data.",feat_adaptive:"Adaptive Scheduling",feat_adaptive_desc:"Learn optimal intervals from maintenance history",feat_predictions:"Sensor Predictions",feat_predictions_desc:"Predict trigger dates from sensor degradation",feat_seasonal:"Seasonal Adjustments",feat_seasonal_desc:"Adjust intervals based on seasonal patterns",feat_environmental:"Environmental Correlation",feat_environmental_desc:"Correlate intervals with temperature/humidity",feat_budget:"Budget Tracking",feat_budget_desc:"Track monthly and yearly maintenance spending",feat_groups:"Task Groups",feat_groups_desc:"Organize tasks into logical groups",feat_checklists:"Checklists",feat_checklists_desc:"Multi-step procedures for task completion",settings_general:"General",settings_default_warning:"Default warning days",settings_panel_enabled:"Sidebar panel",settings_panel_title:"Sidebar panel title",settings_notifications:"Notifications",settings_notify_service:"Notification service",settings_install_assist_sentences:"Install Assist sentences",settings_install_assist_sentences_hint:"Copies the voice sentences into your configuration so the classic Assist agent recognises them. A file you edited yourself is never overwritten.",test_notification:"Test notification",send_test:"Send test",testing:"Sending\u2026",test_notification_success:"Test notification sent",test_notification_failed:"Test notification failed",notify_per_person:"Per-person delivery",notify_no_own_device:"No own device \u2014 uses the household service",settings_notify_due_soon:"Notify when due soon",settings_notify_overdue:"Notify when overdue",settings_notify_triggered:"Notify when triggered",settings_interval_hours:"Repeat interval (hours, 0 = once)",settings_quiet_hours:"Quiet hours",settings_quiet_start:"Start",settings_quiet_end:"End",settings_max_per_day:"Max notifications per day (0 = unlimited)",settings_bundling:"Bundle notifications",settings_bundle_threshold:"Bundle threshold",settings_reminder_leads:"Extra reminders (days before due)",settings_reminder_leads_hint:"Comma-separated lead times, e.g. 14, 3, 0 \u2014 one extra reminder fires on each matching day. Empty = off.",settings_actions:"Mobile Action Buttons",settings_action_complete:"Show 'Complete' button",settings_action_skip:"Show 'Skip' button",settings_action_snooze:"Show 'Snooze' button",settings_weekly_digest:"Weekly digest",settings_weekly_digest_hint:"A single summary notification on Monday morning when tasks are due.",settings_warranty_reminder:"Warranty expiry reminder",settings_warranty_reminder_days:"Days before expiry",settings_warranty_reminder_hint:"Notify once when an object's warranty is this many days from expiring.",settings_snooze_hours:"Snooze duration (hours)",settings_budget:"Budget",settings_currency:"Currency",settings_budget_monthly:"Monthly budget",settings_budget_yearly:"Yearly budget",settings_budget_alerts:"Budget alerts",settings_budget_threshold:"Alert threshold (%)",settings_import_export:"Import / Export",settings_export_json:"Export JSON",settings_export_yaml:"Export YAML",settings_export_csv:"Export CSV",settings_import_csv:"Import CSV",settings_import_placeholder:"Paste JSON or CSV content here\u2026",settings_import_btn:"Import",settings_import_success:"{count} objects imported successfully.",settings_export_success:"Export downloaded.",settings_saved:"Setting saved.",settings_include_history:"Include history",settings_export_selection:"Limit to selected objects (optional)",settings_docs_archive:"Documents archive (with files)",settings_docs_archive_hint:"The JSON/YAML/CSV exports carry settings only. This ZIP includes the uploaded file contents so a restore is complete.",settings_docs_export_btn:"Download documents ZIP",settings_docs_import_btn:"Restore documents ZIP",settings_docs_import_success:"Restored: {blobs} files, {docs} documents",sort_alphabetical:"Alphabetical",sort_due_soonest:"Due soonest",sort_task_count:"Task count",sort_area:"Area",sort_assigned_user:"Assigned user",sort_group:"Group",groupby_none:"No grouping",groupby_area:"By area",groupby_group:"By group",groupby_user:"By user",filter_label:"Filter",user_label:"User",photo_label:"Photo",sort_label:"Sort",group_by_label:"Group by",state_value_help:'Use the HA state value (usually lowercase, e.g. "on"/"off"). Case is normalised on save.',target_changes_help:"Number of matching transitions before the trigger fires (default: 1).",qr_print_title:"Print QR codes",qr_print_desc:"Generate a printable page of QR codes to cut out and stick on your equipment.",qr_print_load:"Load objects",qr_print_filter:"Filter",qr_print_objects:"Objects",qr_print_actions:"Actions",qr_print_url_mode:"Link type",qr_print_estimate:"Estimated QR codes",qr_print_over_limit:"cap is 200, narrow the filter",qr_print_generate:"Generate QR codes",qr_print_generating:"Generating\u2026",qr_print_ready:"QR codes ready",qr_print_print_button:"Print",qr_print_empty:"Nothing to generate",qr_action_skip:"Skip",vacation_title:"Vacation mode",vacation_active:"active",vacation_ended:"ended",vacation_desc:"Plan a vacation: notifications are paused during the period plus a buffer of days. You can opt specific tasks back in.",vacation_enable:"Enable vacation mode",vacation_start:"Start",vacation_end:"End",vacation_buffer:"Buffer (days)",vacation_exempt_title:"Notify anyway during vacation",vacation_exempt_desc:"Pick tasks that should still notify during vacation (e.g. critical pool chemistry).",vacation_load_tasks:"Load tasks",vacation_preview_btn:"Show preview",vacation_preview_affected:"tasks affected",vacation_event_due_soon:"becomes due soon",vacation_event_overdue:"becomes overdue",vacation_event_triggered_est:"sensor trigger possible",vacation_sensor_based:"(sensor-based)",vacation_action_notify:"Notify anyway",vacation_action_unsilence:"Silence again",vacation_marked_complete:"Marked complete",vacation_marked_skip:"Skipped",vacation_end_now:"End vacation now",add:"Add",show_stats:"Show stats + graphs",hide_stats:"Hide stats",adaptive_no_data:"Not enough completion history yet for adaptive analysis. Complete this task a few more times to unlock interval recommendations and reliability charts.",suggestion_applied:"Suggested interval applied",vacation_mode:"Vacation mode",vacation_status_active:"Active now",vacation_status_scheduled:"Scheduled",vacation_status_inactive:"Inactive",vacation_end_now_confirm:"End vacation immediately?",vacation_exempt_count:"exempt",vacation_advanced:"Advanced\u2026",vacation_open_panel:"Open in panel",enable:"Enable",saved:"Saved",budget_monthly_set:"Set monthly",budget_yearly_set:"Set yearly",budget_advanced:"Currency, alerts\u2026",budget_open_panel:"Open in panel",groups_empty:"No groups yet.",group_new_placeholder:"Add group\u2026",group_delete_confirm:'Delete group "{name}"?',groups_manage_tasks:"Manage task assignments\u2026",groups_open_panel:"Open in panel",unassigned:"Unassigned",no_area:"No area",has_overdue:"Has overdue tasks",object:"Object",settings_panel_access:"Panel access",settings_panel_access_desc:"Admins always have full access. To delegate create, edit and delete to specific non-admins, switch this on and pick them below \u2014 everyone else sees only Complete and Skip.",settings_operator_write:"Allow selected users to create, edit & delete",settings_operator_write_desc:"Off: only admins can change content. On: the selected users below get full access too.",no_non_admin_users:"No non-admin users found. Add some in Settings \u2192 People.",owner_label:"Owner",feat_completion_actions:"Completion actions",feat_completion_actions_desc:"Per-task HA action on complete + quick-complete QR with pre-set values.",on_complete_action_title:"On complete: trigger HA action (optional)",on_complete_action_desc:"Calls an HA service when the task is completed \u2014 e.g. reset a counter on the device.",on_complete_action_service:"Service",on_complete_action_target:"Target entity",on_complete_action_target_hint:"Note: the entity domain must match the service \u2014 e.g. 'button.press' only works on button.*, 'counter.increment' only on counter.*, 'input_button.press' only on input_button.* etc. On a mismatch the action will silently fail (HA logs 'Referenced entities ... missing or not currently available').",on_complete_action_data:"Data (JSON, optional)",on_complete_action_test:"Validate configuration",on_complete_action_test_success:"\u2713 Configuration valid (action will fire only on task completion)",on_complete_action_test_failed:"Failed",quick_complete_defaults_title:"Quick-complete defaults (for QR scans, optional)",quick_complete_defaults_desc:"Pre-set values for quick-complete QR scans. Without these, the QR opens the complete dialog.",quick_complete_defaults_notes:"Notes",quick_complete_defaults_cost:"Cost",quick_complete_defaults_duration:"Duration (minutes)",quick_complete_defaults_feedback_none:"No feedback",quick_complete_defaults_feedback_needed:"Was needed",quick_complete_defaults_feedback_not_needed:"Not needed",quick_complete_success:"Quickly marked complete",show_all_objects:"Show all objects",show_all_tasks:"Clear filter \u2014 show all tasks",filter_to_overdue:"Filter task list to overdue only",filter_to_due_soon:"Filter task list to due-soon only",filter_to_triggered:"Filter task list to triggered only",open_task:"Open task",show_details:"Show history + stats",hide_details:"Hide details",history_empty:"No history yet.",history_edit_button:"Edit entry",total_cost:"Total cost",times_performed:"Performed",older_entries:"older",open_in_panel:"Open in Maintenance panel",skip_reason:"Skip reason (optional)",reset_to_date:"Reset last_performed to",delete_task_confirm:"Delete this task and its history?",delete_object_confirm:"Delete this object and all its tasks?",loading:"Loading\u2026",archive:"Archive",undo:"Undo",task_archived:"Task archived",object_archived:"Object archived",unarchive:"Unarchive",archived:"Archived",show_archived:"Show archived",hide_archived:"Hide archived",confirm_archive_object:"Archive this object and its tasks? They keep their history and can be unarchived later.",settings_archive:"Archive & Retention",settings_archive_desc:"Retire completed one-off tasks without deleting them. Archived items are hidden and inert but keep their history and cost.",settings_archive_oneoff_days:"Auto-archive completed one-off tasks after (days, 0 = off)",settings_delete_archived_oneoff_days:"Auto-delete archived one-off tasks after (days, 0 = never)",archive_object:"Archive object",unarchive_object:"Unarchive object",documents:"Documents",documents_empty:"No documents yet.",doc_upload:"Upload file",doc_uploading:"Uploading\u2026",doc_add_link:"Add link",doc_link_url:"URL (https://\u2026)",doc_link_title:"Title (optional)",doc_open:"Open",doc_delete_confirm:'Delete "{name}"?',doc_too_large:"File is too large (max 25 MB).",doc_upload_failed:"Upload failed.",completion_photo_optional:"Completion photo (optional)",add_photo:"Add photo",uploading:"Uploading\u2026",remove:"Remove",doc_deduped:"Already stored elsewhere \u2014 shared, no extra space used.",doc_dup_in_object:"This file is already attached to this object.",doc_link_invalid:"Only http/https links are allowed.",doc_cat_manual:"Manual",doc_cat_warranty:"Warranty",doc_cat_invoice:"Invoice",doc_cat_spare_parts:"Spare parts",doc_cat_photo:"Photo",doc_cat_other:"Other",doc_link_badge:"Link",doc_storage_title:"Document storage",doc_storage_saved:"Saved via deduplication",doc_storage_refresh:"Refresh",doc_download:"Download",doc_close:"Close",doc_camera:"Take photo",doc_drop_hint:"Drop files here",doc_task_none:"No documents linked to this task.",doc_link_existing:"Link a document\u2026",doc_attach:"Link",doc_unlink:"Unlink",doc_page:"Page",chart_range_7d:"7d",chart_range_30d:"30d",chart_range_90d:"90d",chart_range_1y:"1y",chart_since_service:"since last service",chart_no_stats:"No long-term statistics for this entity \u2014 showing maintenance-event values only",auto_complete_on_recovery:"Auto-complete when the sensor recovers",auto_complete_on_recovery_help:"Records a completion (sets last performed) when the trigger clears itself \u2014 e.g. salt refilled, filter replaced.",doc_search:"Search documents\u2026",doc_search_none:"No matching documents",link_device_optional:"Link to existing device (optional)",parent_object_optional:"Parent object (optional)",parent_none:"(No parent)",paused:"Paused",pause_object:"Pause",resume_object:"Resume",pause_until_prompt:"Freeze this object's schedules \u2014 nothing becomes due and nothing notifies until it is resumed. Optionally set an auto-resume date.",pause_until_label:"Resume on (optional)",object_paused:"Object paused",object_resumed:"Object resumed \u2014 schedules restarted",object_paused_badge:"Paused",paused_until_label:"until",replace_object:"Replace\u2026",replace_object_prompt:"Retire this object and create a successor. History and costs stay archived on the old one; tasks and documents carry over to the new one, counters start fresh.",replace_name_label:"Successor name",object_replaced:"Object replaced \u2014 successor created",reading_unit_label:"Reading unit (e.g. kWh, m\xB3)",reading_unit_help:"Shown next to the recorded value when completing this task.",reading_value_label:"Reading value",reading_label:"Reading",settings_templates_label:"Template gallery",settings_templates_hint:`Untick templates you'll never need \u2014 they disappear from the "From template" pickers (panel and config flow). Nothing else changes; you can re-enable them any time.`,worksheet:"Work sheet",worksheet_scan_view:"Scan to open the task",worksheet_scan_complete:"Scan to complete",worksheet_manual_excerpt:"Manual excerpt",worksheet_pages:"pages",worksheet_printed:"Printed",worksheet_never:"Never",card_all_caught_up:"All caught up \u2014 nothing needs attention",postpone:"Postpone",postpone_date_prompt:"Postpone this occurrence to which date?",postpone_date_label:"New due date",postponed:"Postponed",postponed_to:"Postponed to",season_window_label:"Seasonal window (months)",season_window_hint:"Only due in the selected months; off-season dates roll to the next active month. None = all year.",series_end_label:"Ends",series_end_never:"Never (repeats indefinitely)",series_end_after_count:"After a number of times",series_end_until:"On a date",series_end_count_label:"Number of times",series_end_until_label:"End date",parts_section:"Parts & consumables",part_add:"Add part",part_name:"Name",part_vendor:"Manufacturer",part_storage_location:"Storage location",part_product_url:"Product URL",part_unit:"Unit",part_cost:"Unit price",part_stock:"Stock",part_reorder_threshold:"Reorder at",part_restock_quantity:"Restock quantity",part_auto_buy:"Auto-create buy task when low",part_restock:"Adjust stock",restock_quantity_label:"Quantity bought",consumes_parts_label:"Consumes parts",shared_parts_other_objects:"Parts from other objects",shared_parts_help:"Several objects can share one stock. Completing this task takes from the owning object.",shared_part_unknown:"Unknown part",parts_load_failed:"Couldn't load this object's parts \u2014 the consumes-parts options are unavailable right now.",adopt_problem_button:"Adopt problem sensors",adopt_problem_title:"Adopt problem sensors",adopt_problem_hint:"Turn HA problem sensors (printer errors, filter warnings, low battery) into maintenance tasks that trigger while the problem is active and clear themselves when it resolves.",adopt_problem_none:"No problem sensors found that aren't already tracked.",adopt_problem_active:"active",adopt_problem_ok:"ok",adopt_problem_new_object:"(new)",adopt_problem_adopt:"Adopt selected",adopt_problem_done:"Adopted {tasks} problem sensor(s)",views_label:"Views",views_none:"\u2014 No view \u2014",views_manage:"Save / manage views",views_dialog_title:"Saved views",views_dialog_hint:"Save the current filters as a named view everyone can reuse.",views_name_placeholder:"View name",views_save_current:"Save current filters",views_none_yet:"No saved views yet.",close:"Close",trigger_hint_now:"The sensor reads {value} right now.",trigger_hint_above:"The task triggers once it rises above {target}.",trigger_hint_below:"It triggers once it falls below {target}.",trigger_hint_counter_delta:"Counts from the current reading ({value}): due at {due} (+{target}), and the count restarts after each completion.",trigger_hint_counter_delta_edit:"Counts usage since the last completion: due after +{target}; the count restarts after each completion.",trigger_hint_counter_abs:"The task becomes due once the sensor reaches {target}.",trigger_hint_runtime:"The task becomes due after {hours} h of accumulated on-time; the counter restarts after each completion.",trigger_hint_state_change:"The task becomes due after {count} state change(s).",trigger_hint_state_change_to:"The task becomes due after {count} change(s) to \u201C{state}\u201D.",trigger_hint_state_now:"Current state: {value}.",adopt_problem_part:"Uses part: {name}",label_filter:"Label",all_labels:"All labels",settings_notify_scope:"Notify only for view",settings_notify_scope_all:"All tasks",settings_notify_scope_hint:"Only tasks matching the selected saved view's label/user filters send reminders. Status, sorting and grouping of the view are ignored here.",card_saved_view:"Saved view",card_saved_view_none:"None",card_saved_view_help:"Applies the view's status, user and label filters on top of the filters above. The view's sorting and grouping are panel display settings and are not applied on the card.",doc_part_none:"No documents linked to this part.",settings_templates_toggle_group:"Enable or disable all templates in this group",setups_button:"Suggested setups",setups_title:"Suggested setups (Beta)",setups_hint:"Devices of supported integrations whose consumable sensors can drive maintenance tasks. Adopting creates the object and wires each task to its sensor \u2014 it triggers when the consumable runs low and resolves itself after replacement.",setups_none:"No supported devices with unwired consumable sensors found.",setups_adopt:"Set up selected",setups_done:"{tasks} sensor-wired tasks created.",complete_parts_used:"Parts used this time",part_delete_confirm:"Delete part '{name}'? Its stock tracking, task links and any open buy reminder will be removed.",baseline_start_value:"Start reading (optional)",baseline_start_help:"Counting starts from this reading. Leave empty to count from the current value; enter the reading at the last service so usage since then already counts.",setups_baseline_hint:"reading at last service (optional)",baseline_start_help_edit:"Leave empty to keep the existing counting. Entering a value re-anchors the counting (e.g. the reading at the last service).",baseline_current_effective:"Currently effective start value: {value}",runtime_on_states:"Active states (comma-separated)",runtime_on_states_help:"States that count as running \u2014 default: on. E.g. mowing, cleaning, printing. With an attribute selected, its values are matched instead.",setups_target_new:"Create new: {name}",schedule_preview_title:"Next dates",schedule_preview_ontime:"Assuming on-time completion.",schedule_preview_ends:"(series ends)",adopt_problem_responsible:"Responsible user for all adopted tasks (optional)",adopt_problem_configure:"Configure",history_auto:"Automatic",battery_fleet_title:"Battery fleet",battery_fleet_none_low:"All batteries OK \u2014 nothing to replace.",battery_fleet_buy_now:"Buy now",battery_fleet_soon:"Needed soon",battery_fleet_soon_hint:"Predicted from the last replacement date \u2014 order ahead.",battery_fleet_mark_all:"Mark all replaced",battery_fleet_mark_one:"Mark this battery replaced",battery_fleet_offline:"offline",battery_fleet_trigger_lost:"This task's sensor trigger was lost \u2014 it will not fire or auto-complete.",battery_fleet_repair:"Repair",battery_fleet_exclude:"Exclude from the fleet",battery_fleet_excluded:"Excluded",battery_fleet_include:"Track again",battery_fleet_total:"{n} batteries tracked",battery_fleet_setup_button:"Battery fleet",battery_fleet_setup_done:"Battery fleet set up \u2014 one task tracks all your batteries.",update_banner:"A newer version of Maintenance Supporter is on the server \u2014 reload to update the panel.",update_reload:"Reload"};var Ve="\u20AC",xe="en",Ke=(()=>{let a=window;return a.__msLocales||(a.__msLocales={store:{},inflight:{}}),a.__msLocales})(),D=Ke.store;D.en||(D.en=Ye);var jt=new Set(["de","nl","fr","it","es","pt","pt-br","ru","uk","pl","cs","sv","zh","da","fi","nb","ja","hi","hu","ko","tr"]),Et="/maintenance_supporter_locales",Q=Ke.inflight;function we(a){let e=(a||xe).toLowerCase();return e.startsWith("pt")&&e.endsWith("br")?"pt-br":e.substring(0,2)}function m(a,e){let t=we(e);return D[t]?.[a]??D.en[a]??a}function Qe(a){let e=we(a);return e===xe||e in D}function Je(a){let e=we(a);return e===xe||e in D||!jt.has(e)?Promise.resolve():(e in Q||(Q[e]=fetch(`${Et}/${e}.json`).then(t=>t.ok?t.json():null).then(t=>{t?D[e]=t:delete Q[e]}).catch(()=>{delete Q[e]})),Q[e])}var Tt=window,Ge=Tt.__msDateTimePrefs??={};function Ze(a){a&&(Ge.date=a.date_format,Ge.time=a.time_format)}function Xe(a,e){if(a==null)return"\u2014";let t=e||"en";return a<0?`${Math.abs(a)} ${m("d_overdue",t)}`:a===0?m("today",t):`${a} ${m(a===1?"day":"days",t)}`}var Oo=k` +Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"{field}: too long (max {n} characters)",err_too_short:"{field}: too short (min {n} characters)",err_value_too_high:"{field}: too large (max {n})",err_value_too_low:"{field}: too small (min {n})",err_required:"{field}: required",err_wrong_type:"{field}: wrong type (expected: {type})",err_invalid_choice:"{field}: not an allowed value",err_invalid_value:"{field}: invalid value",feat_schedule_time:"Time-of-day scheduling",feat_schedule_time_desc:"Tasks become overdue at a specific time of day instead of midnight.",schedule_time_optional:"Due at time (optional, HH:MM)",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",days:"days",day:"day",today:"Today",d_overdue:"d overdue",no_tasks:"No maintenance tasks yet. Create an object to get started.",no_tasks_short:"No tasks",no_history:"No history entries yet.",show_all:"Show all",cost_duration_chart:"Cost & Duration",installed:"Installed",confirm_delete_object:"Delete this object and all its tasks?",confirm_delete_task:"Delete this task?",min:"Min",max:"Max",save:"Save",saving:"Saving\u2026",edit_task:"Edit Task",new_task:"New Maintenance Task",task_name:"Task name",maintenance_type:"Maintenance type",priority:"Priority",labels:"Labels",labels_placeholder:"e.g. safety, seasonal, tenant-visible",labels_help:"Comma-separated tags for filtering and reporting.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",schedule_type:"Schedule type",interval_days:"Interval (days)",warning_days:"Warning days",earliest_completion_days:"Earliest completion (days before due)",earliest_completion_days_help:"Leave empty to allow completing any time. 0 = only on/after the due date.",last_performed_optional:"Last performed (optional)",interval_anchor:"Interval anchor",anchor_completion:"From completion date",anchor_planned:"From planned date (no drift)",edit_object:"Edit Object",name:"Name",manufacturer_optional:"Manufacturer (optional)",model_optional:"Model (optional)",serial_number_optional:"Serial number (optional)",serial_number_label:"S/N",documentation_url_label:"Manual",object_notes_label:"Notes",sort_due_date:"Due date",sort_object:"Object name",sort_type:"Type",sort_task_name:"Task name",all_objects:"All objects",tasks_lower:"tasks",no_tasks_yet:"No tasks yet",add_first_task:"Add first task",trigger_configuration:"Trigger Configuration",entity_id:"Entity ID",comma_separated:"comma-separated",entity_logic:"Entity logic",entity_logic_any:"Any entity triggers",entity_logic_all:"All entities must trigger",entities:"entities",attribute_optional:"Attribute (optional, blank = state)",use_entity_state:"Use entity state (no attribute)",trigger_above:"Trigger above",trigger_below:"Trigger below",for_at_least_minutes:"For at least (minutes)",safety_interval_days:"Safety interval (days, optional)",safety_interval:"Safety interval (optional)",delta_mode:"Delta mode",from_state_optional:"From state (optional)",to_state_optional:"To state (optional)",documentation_url_optional:"Documentation URL (optional)",object_notes_optional:"Notes (optional)",nfc_tag_id_optional:"NFC Tag ID (optional)",nfc_tags_empty_help:"No NFC tags registered in Home Assistant yet.",nfc_tags_open_settings:"Open Tags settings",nfc_tags_refresh:"Refresh",environmental_entity_optional:"Environmental sensor (optional)",environmental_entity_helper:"e.g. sensor.outdoor_temperature \u2014 adjusts the interval based on environmental conditions",environmental_attribute_optional:"Environmental attribute (optional)",nfc_tag_id:"NFC Tag ID",nfc_linked:"NFC tag linked",nfc_link_hint:"Click to link NFC tag",responsible_user:"Responsible User",shared_with:"Shared with (rotation)",shared_with_help:"Pick multiple people to share this task; the responsible person rotates on each completion.",rotation_strategy:"Rotation",rotation_none:"No rotation",rotation_round_robin:"Round-robin",rotation_least_completed:"Least completed",rotation_random:"Random",no_user_assigned:"(No user assigned)",all_users:"All Users",my_tasks:"My Tasks",tab_calendar:"Calendar",cal_no_events:"No maintenance",cal_window_7:"7 days",cal_window_14:"14 days",cal_window_30:"30 days",cal_window_365:"1 year",cal_every_n_days:"every {n} days",cal_source_time:"Time-based",cal_source_time_adaptive:"Time-based (adaptive)",cal_source_sensor:"Sensor-based",cal_predicted:"predicted",cal_confidence_high:"high confidence",cal_confidence_medium:"medium confidence",cal_confidence_low:"low confidence",budget_monthly:"Monthly budget",budget_yearly:"Yearly budget",groups:"Groups",new_group:"New group",edit_group:"Edit group",no_groups:"No groups yet",delete_group:"Delete group",delete_group_confirm:"Delete group '{name}'?",group_select_tasks:"Select tasks",group_name_required:"Name is required",description_optional:"Description (optional)",selected:"Selected",loading_chart:"Loading chart data...",hide_outliers:"Hide outliers (sensor glitches)",was_maintenance_needed:"Was this maintenance needed?",feedback_needed:"Needed",feedback_not_needed:"Not needed",feedback_not_sure:"Not sure",suggested_interval:"Suggested interval",apply_suggestion:"Apply",reanalyze:"Re-analyze",reanalyze_result:"New analysis",reanalyze_insufficient_data:"Not enough data to produce a recommendation",data_points:"data points",dismiss_suggestion:"Dismiss",confidence_low:"Low",confidence_medium:"Medium",confidence_high:"High",recommended:"recommended",seasonal_awareness:"Seasonal Awareness",edit_seasonal_overrides:"Edit seasonal factors",seasonal_overrides_title:"Seasonal factors (override)",seasonal_overrides_hint:"Factor per month (0.1\u20135.0). Empty = learned automatically.",seasonal_override_invalid:"Invalid value",seasonal_override_range:"Factor must be between 0.1 and 5.0",clear_all:"Clear all",seasonal_chart_title:"Seasonal Factors",seasonal_learned:"Learned",seasonal_manual:"Manual",month_jan:"Jan",month_feb:"Feb",month_mar:"Mar",month_apr:"Apr",month_may:"May",month_jun:"Jun",month_jul:"Jul",month_aug:"Aug",month_sep:"Sep",month_oct:"Oct",month_nov:"Nov",month_dec:"Dec",sensor_prediction:"Sensor Prediction",degradation_trend:"Trend",trend_rising:"Rising",trend_falling:"Falling",trend_stable:"Stable",trend_insufficient_data:"Insufficient data",days_until_threshold:"Days until threshold",threshold_exceeded:"Threshold exceeded",environmental_adjustment:"Environmental factor",sensor_prediction_urgency:"Sensor predicts threshold in ~{days} days",day_short:"day",weibull_reliability_curve:"Reliability Curve",weibull_failure_probability:"Failure Probability",weibull_r_squared:"Fit R\xB2",beta_early_failures:"Early Failures",beta_random_failures:"Random Failures",beta_wear_out:"Wear-out",beta_highly_predictable:"Highly Predictable",confidence_interval:"Confidence Interval",confidence_conservative:"Conservative",confidence_aggressive:"Optimistic",current_interval_marker:"Current interval",recommended_marker:"Recommended",characteristic_life:"Characteristic life",chart_mini_sparkline:"Trend sparkline",chart_history:"Cost and duration history",chart_seasonal:"Seasonal factors, 12 months",chart_weibull:"Weibull reliability curve",chart_sparkline:"Sensor trigger value chart",days_progress:"Days progress",qr_code:"QR Code",qr_generating:"Generating QR code\u2026",qr_error:"Failed to generate QR code.",qr_error_no_url:"No HA URL configured. Please set an external or internal URL in Settings \u2192 System \u2192 Network.",save_error:"Failed to save. Please try again.",qr_print:"Print",qr_download:"Download SVG",qr_action:"Action on scan",qr_action_view:"View maintenance info",qr_action_complete:"Mark maintenance as complete",qr_url_mode:"Link type",qr_mode_companion:"Companion App",qr_mode_local:"Local (mDNS)",qr_mode_server:"Server URL",overview:"Overview",analysis:"Analysis",recent_activities:"Recent Activities",search_notes:"Search notes",avg_cost:"Avg Cost",no_advanced_features:"No advanced features enabled",no_advanced_features_hint:"Enable \u201CAdaptive Intervals\u201D or \u201CSeasonal Patterns\u201D in the integration settings to see analysis data here.",analysis_not_enough_data:"Not enough data for analysis yet.",analysis_not_enough_data_hint:"Weibull analysis requires at least 5 completed maintenances; seasonal patterns become visible after 6+ data points per month.",analysis_manual_task_hint:"Manual tasks without an interval do not generate analysis data.",completions:"completions",current:"Current",shorter:"Shorter",longer:"Longer",normal:"Normal",disabled:"Disabled",compound_logic:"Compound logic",compound:"Compound (multiple conditions)",compound_logic_and:"AND \u2014 all conditions must trigger",compound_logic_or:"OR \u2014 any condition triggers",compound_help:"Combine several sensor conditions into one trigger.",compound_no_conditions:"No conditions yet \u2014 add at least one.",compound_add_condition:"Add condition",compound_condition:"Condition",compound_remove_condition:"Remove condition",card_title:"Title",card_show_header:"Show header with statistics",card_show_actions:"Show action buttons",card_compact:"Compact mode",card_max_items:"Max items (0 = all)",card_filter_status:"Filter by status",card_filter_status_help:"Empty = show all statuses.",card_filter_objects:"Filter by objects",card_filter_objects_help:"Empty = show all objects.",card_filter_areas:"Filter by areas",card_filter_areas_help:"Empty = show all areas.",card_filter_entities:"Filter by entities (entity_ids)",card_filter_entities_help:"Pick sensor / binary_sensor entities from this integration. Empty = all.",card_loading_objects:"Loading objects\u2026",card_load_error:"Could not load objects \u2014 check the WebSocket connection.",card_no_tasks_title:"No maintenance tasks yet",card_no_tasks_cta:"\u2192 Create one in the Maintenance panel",no_objects:"No objects yet.",action_error:"Action failed. Please try again.",area_id_optional:"Area (optional)",installation_date_optional:"Installation date (optional)",warranty_expiry_optional:"Warranty expiry (optional)",warranty:"Warranty",warranty_valid_until:"valid until {date}",warranty_expires_in:"expires in {days} days",warranty_expired:"expired",cal_past_windows:"Past windows",cal_forward_windows:"Forward windows",history_edit_title:"Edit history entry",history_edit_timestamp:"Timestamp",manufacturer:"Manufacturer",model:"Model",area:"Area",actions:"Actions",view_mode_label:"View",view_cards:"Card view",view_table:"Table view",objects_table_columns_label:"Objects table columns",objects_table_columns_hint:"Choose which columns appear in the objects table view.",custom_icon_optional:"Icon (optional, e.g. mdi:wrench)",task_enabled:"Task enabled",skip_reason_prompt:"Skip this task?",reason_optional:"Reason (optional)",reset_date_prompt:"Mark task as performed?",reset_date_optional:"Last performed date (optional, defaults to today)",notes_label:"Notes",documentation_label:"Documentation",no_nfc_tag:"\u2014 No tag \u2014",dashboard:"Dashboard",tab_today:"Today",palette_placeholder:"Search objects and tasks\u2026",palette_no_results:"No matches",palette_hint:"\u2191\u2193 to navigate \xB7 Enter to open \xB7 Esc to close",today_all_caught_up:"All caught up! Nothing due this week.",today_overdue:"Overdue",today_due_today:"Due today",today_this_week:"This week",settings:"Settings",settings_features:"Advanced Features",settings_features_desc:"Enable or disable advanced features. Disabling hides them from the UI but does not delete data.",feat_adaptive:"Adaptive Scheduling",feat_adaptive_desc:"Learn optimal intervals from maintenance history",feat_predictions:"Sensor Predictions",feat_predictions_desc:"Predict trigger dates from sensor degradation",feat_seasonal:"Seasonal Adjustments",feat_seasonal_desc:"Adjust intervals based on seasonal patterns",feat_environmental:"Environmental Correlation",feat_environmental_desc:"Correlate intervals with temperature/humidity",feat_budget:"Budget Tracking",feat_budget_desc:"Track monthly and yearly maintenance spending",feat_groups:"Task Groups",feat_groups_desc:"Organize tasks into logical groups",feat_checklists:"Checklists",feat_checklists_desc:"Multi-step procedures for task completion",settings_general:"General",settings_default_warning:"Default warning days",settings_panel_enabled:"Sidebar panel",settings_panel_title:"Sidebar panel title",settings_notifications:"Notifications",settings_notify_service:"Notification service",settings_install_assist_sentences:"Install Assist sentences",settings_install_assist_sentences_hint:"Copies the voice sentences into your configuration so the classic Assist agent recognises them. A file you edited yourself is never overwritten.",test_notification:"Test notification",send_test:"Send test",testing:"Sending\u2026",test_notification_success:"Test notification sent",test_notification_failed:"Test notification failed",notify_per_person:"Per-person delivery",notify_no_own_device:"No own device \u2014 uses the household service",settings_notify_due_soon:"Notify when due soon",settings_notify_overdue:"Notify when overdue",settings_notify_triggered:"Notify when triggered",settings_interval_hours:"Repeat interval (hours, 0 = once)",settings_quiet_hours:"Quiet hours",settings_quiet_start:"Start",settings_quiet_end:"End",settings_max_per_day:"Max notifications per day (0 = unlimited)",settings_bundling:"Bundle notifications",settings_bundle_threshold:"Bundle threshold",settings_reminder_leads:"Extra reminders (days before due)",settings_reminder_leads_hint:"Comma-separated lead times, e.g. 14, 3, 0 \u2014 one extra reminder fires on each matching day. Empty = off.",settings_actions:"Mobile Action Buttons",settings_action_complete:"Show 'Complete' button",settings_action_skip:"Show 'Skip' button",settings_action_snooze:"Show 'Snooze' button",settings_weekly_digest:"Weekly digest",settings_weekly_digest_hint:"A single summary notification on Monday morning when tasks are due.",settings_warranty_reminder:"Warranty expiry reminder",settings_warranty_reminder_days:"Days before expiry",settings_warranty_reminder_hint:"Notify once when an object's warranty is this many days from expiring.",settings_snooze_hours:"Snooze duration (hours)",settings_budget:"Budget",settings_currency:"Currency",settings_budget_monthly:"Monthly budget",settings_budget_yearly:"Yearly budget",settings_budget_alerts:"Budget alerts",settings_budget_threshold:"Alert threshold (%)",settings_import_export:"Import / Export",settings_export_json:"Export JSON",settings_export_yaml:"Export YAML",settings_export_csv:"Export CSV",settings_import_csv:"Import CSV",settings_import_placeholder:"Paste JSON or CSV content here\u2026",settings_import_btn:"Import",settings_import_success:"{count} objects imported successfully.",settings_export_success:"Export downloaded.",settings_saved:"Setting saved.",settings_include_history:"Include history",settings_export_selection:"Limit to selected objects (optional)",settings_docs_archive:"Documents archive (with files)",settings_docs_archive_hint:"The JSON/YAML/CSV exports carry settings only. This ZIP includes the uploaded file contents so a restore is complete.",settings_docs_export_btn:"Download documents ZIP",settings_docs_import_btn:"Restore documents ZIP",settings_docs_import_success:"Restored: {blobs} files, {docs} documents",sort_alphabetical:"Alphabetical",sort_due_soonest:"Due soonest",sort_task_count:"Task count",sort_area:"Area",sort_assigned_user:"Assigned user",sort_group:"Group",groupby_none:"No grouping",groupby_area:"By area",groupby_group:"By group",groupby_user:"By user",filter_label:"Filter",user_label:"User",photo_label:"Photo",sort_label:"Sort",group_by_label:"Group by",state_value_help:'Use the HA state value (usually lowercase, e.g. "on"/"off"). Case is normalised on save.',target_changes_help:"Number of matching transitions before the trigger fires (default: 1).",qr_print_title:"Print QR codes",qr_print_desc:"Generate a printable page of QR codes to cut out and stick on your equipment.",qr_print_load:"Load objects",qr_print_filter:"Filter",qr_print_objects:"Objects",qr_print_actions:"Actions",qr_print_url_mode:"Link type",qr_print_estimate:"Estimated QR codes",qr_print_over_limit:"cap is 200, narrow the filter",qr_print_generate:"Generate QR codes",qr_print_generating:"Generating\u2026",qr_print_ready:"QR codes ready",qr_print_print_button:"Print",qr_print_empty:"Nothing to generate",qr_action_skip:"Skip",vacation_title:"Vacation mode",vacation_active:"active",vacation_ended:"ended",vacation_desc:"Plan a vacation: notifications are paused during the period plus a buffer of days. You can opt specific tasks back in.",vacation_enable:"Enable vacation mode",vacation_start:"Start",vacation_end:"End",vacation_buffer:"Buffer (days)",vacation_exempt_title:"Notify anyway during vacation",vacation_exempt_desc:"Pick tasks that should still notify during vacation (e.g. critical pool chemistry).",vacation_load_tasks:"Load tasks",vacation_preview_btn:"Show preview",vacation_preview_affected:"tasks affected",vacation_event_due_soon:"becomes due soon",vacation_event_overdue:"becomes overdue",vacation_event_triggered_est:"sensor trigger possible",vacation_sensor_based:"(sensor-based)",vacation_action_notify:"Notify anyway",vacation_action_unsilence:"Silence again",vacation_marked_complete:"Marked complete",vacation_marked_skip:"Skipped",vacation_end_now:"End vacation now",add:"Add",show_stats:"Show stats + graphs",hide_stats:"Hide stats",adaptive_no_data:"Not enough completion history yet for adaptive analysis. Complete this task a few more times to unlock interval recommendations and reliability charts.",suggestion_applied:"Suggested interval applied",vacation_mode:"Vacation mode",vacation_status_active:"Active now",vacation_status_scheduled:"Scheduled",vacation_status_inactive:"Inactive",vacation_end_now_confirm:"End vacation immediately?",vacation_exempt_count:"exempt",vacation_advanced:"Advanced\u2026",vacation_open_panel:"Open in panel",enable:"Enable",saved:"Saved",budget_monthly_set:"Set monthly",budget_yearly_set:"Set yearly",budget_advanced:"Currency, alerts\u2026",budget_open_panel:"Open in panel",groups_empty:"No groups yet.",group_new_placeholder:"Add group\u2026",group_delete_confirm:'Delete group "{name}"?',groups_manage_tasks:"Manage task assignments\u2026",groups_open_panel:"Open in panel",unassigned:"Unassigned",no_area:"No area",has_overdue:"Has overdue tasks",object:"Object",settings_panel_access:"Panel access",settings_panel_access_desc:"Admins always have full access. To delegate create, edit and delete to specific non-admins, switch this on and pick them below \u2014 everyone else sees only Complete and Skip.",settings_operator_write:"Allow selected users to create, edit & delete",settings_operator_write_desc:"Off: only admins can change content. On: the selected users below get full access too.",no_non_admin_users:"No non-admin users found. Add some in Settings \u2192 People.",owner_label:"Owner",feat_completion_actions:"Completion actions",feat_completion_actions_desc:"Per-task HA action on complete + quick-complete QR with pre-set values.",on_complete_action_title:"On complete: trigger HA action (optional)",on_complete_action_desc:"Calls an HA service when the task is completed \u2014 e.g. reset a counter on the device.",on_complete_action_service:"Service",on_complete_action_target:"Target entity",on_complete_action_target_hint:"Note: the entity domain must match the service \u2014 e.g. 'button.press' only works on button.*, 'counter.increment' only on counter.*, 'input_button.press' only on input_button.* etc. On a mismatch the action will silently fail (HA logs 'Referenced entities ... missing or not currently available').",on_complete_action_data:"Data (JSON, optional)",on_complete_action_test:"Validate configuration",on_complete_action_test_success:"\u2713 Configuration valid (action will fire only on task completion)",on_complete_action_test_failed:"Failed",quick_complete_defaults_title:"Quick-complete defaults (for QR scans, optional)",quick_complete_defaults_desc:"Pre-set values for quick-complete QR scans. Without these, the QR opens the complete dialog.",quick_complete_defaults_notes:"Notes",quick_complete_defaults_cost:"Cost",quick_complete_defaults_duration:"Duration (minutes)",quick_complete_defaults_feedback_none:"No feedback",quick_complete_defaults_feedback_needed:"Was needed",quick_complete_defaults_feedback_not_needed:"Not needed",quick_complete_success:"Quickly marked complete",show_all_objects:"Show all objects",show_all_tasks:"Clear filter \u2014 show all tasks",filter_to_overdue:"Filter task list to overdue only",filter_to_due_soon:"Filter task list to due-soon only",filter_to_triggered:"Filter task list to triggered only",open_task:"Open task",show_details:"Show history + stats",hide_details:"Hide details",history_empty:"No history yet.",history_edit_button:"Edit entry",total_cost:"Total cost",times_performed:"Performed",older_entries:"older",open_in_panel:"Open in Maintenance panel",skip_reason:"Skip reason (optional)",reset_to_date:"Reset last_performed to",delete_task_confirm:"Delete this task and its history?",delete_object_confirm:"Delete this object and all its tasks?",loading:"Loading\u2026",archive:"Archive",undo:"Undo",task_archived:"Task archived",object_archived:"Object archived",unarchive:"Unarchive",archived:"Archived",show_archived:"Show archived",hide_archived:"Hide archived",confirm_archive_object:"Archive this object and its tasks? They keep their history and can be unarchived later.",settings_archive:"Archive & Retention",settings_archive_desc:"Retire completed one-off tasks without deleting them. Archived items are hidden and inert but keep their history and cost.",settings_archive_oneoff_days:"Auto-archive completed one-off tasks after (days, 0 = off)",settings_delete_archived_oneoff_days:"Auto-delete archived one-off tasks after (days, 0 = never)",archive_object:"Archive object",unarchive_object:"Unarchive object",documents:"Documents",documents_empty:"No documents yet.",doc_upload:"Upload file",doc_uploading:"Uploading\u2026",doc_add_link:"Add link",doc_link_url:"URL (https://\u2026)",doc_link_title:"Title (optional)",doc_open:"Open",doc_delete_confirm:'Delete "{name}"?',doc_too_large:"File is too large (max 25 MB).",doc_upload_failed:"Upload failed.",completion_photo_optional:"Completion photo (optional)",add_photo:"Add photo",uploading:"Uploading\u2026",remove:"Remove",doc_deduped:"Already stored elsewhere \u2014 shared, no extra space used.",doc_dup_in_object:"This file is already attached to this object.",doc_link_invalid:"Only http/https links are allowed.",doc_cat_manual:"Manual",doc_cat_warranty:"Warranty",doc_cat_invoice:"Invoice",doc_cat_spare_parts:"Spare parts",doc_cat_photo:"Photo",doc_cat_other:"Other",doc_link_badge:"Link",doc_storage_title:"Document storage",doc_storage_saved:"Saved via deduplication",doc_storage_refresh:"Refresh",doc_download:"Download",doc_close:"Close",doc_camera:"Take photo",doc_drop_hint:"Drop files here",doc_task_none:"No documents linked to this task.",doc_link_existing:"Link a document\u2026",doc_attach:"Link",doc_unlink:"Unlink",doc_page:"Page",chart_range_7d:"7d",chart_range_30d:"30d",chart_range_90d:"90d",chart_range_1y:"1y",chart_since_service:"since last service",chart_no_stats:"No long-term statistics for this entity \u2014 showing maintenance-event values only",auto_complete_on_recovery:"Auto-complete when the sensor recovers",auto_complete_on_recovery_help:"Records a completion (sets last performed) when the trigger clears itself \u2014 e.g. salt refilled, filter replaced.",doc_search:"Search documents\u2026",doc_search_none:"No matching documents",link_device_optional:"Link to existing device (optional)",parent_object_optional:"Parent object (optional)",parent_none:"(No parent)",paused:"Paused",pause_object:"Pause",resume_object:"Resume",pause_until_prompt:"Freeze this object's schedules \u2014 nothing becomes due and nothing notifies until it is resumed. Optionally set an auto-resume date.",pause_until_label:"Resume on (optional)",object_paused:"Object paused",object_resumed:"Object resumed \u2014 schedules restarted",object_paused_badge:"Paused",paused_until_label:"until",replace_object:"Replace\u2026",replace_object_prompt:"Retire this object and create a successor. History and costs stay archived on the old one; tasks and documents carry over to the new one, counters start fresh.",replace_name_label:"Successor name",object_replaced:"Object replaced \u2014 successor created",reading_unit_label:"Reading unit (e.g. kWh, m\xB3)",reading_unit_help:"Shown next to the recorded value when completing this task.",reading_value_label:"Reading value",reading_label:"Reading",settings_templates_label:"Template gallery",settings_templates_hint:`Untick templates you'll never need \u2014 they disappear from the "From template" pickers (panel and config flow). Nothing else changes; you can re-enable them any time.`,worksheet:"Work sheet",worksheet_scan_view:"Scan to open the task",worksheet_scan_complete:"Scan to complete",worksheet_manual_excerpt:"Manual excerpt",worksheet_pages:"pages",worksheet_printed:"Printed",worksheet_never:"Never",card_all_caught_up:"All caught up \u2014 nothing needs attention",postpone:"Postpone",postpone_date_prompt:"Postpone this occurrence to which date?",postpone_date_label:"New due date",postponed:"Postponed",postponed_to:"Postponed to",season_window_label:"Seasonal window (months)",season_window_hint:"Only due in the selected months; off-season dates roll to the next active month. None = all year.",series_end_label:"Ends",series_end_never:"Never (repeats indefinitely)",series_end_after_count:"After a number of times",series_end_until:"On a date",series_end_count_label:"Number of times",series_end_until_label:"End date",parts_section:"Parts & consumables",part_add:"Add part",part_name:"Name",part_vendor:"Manufacturer",part_storage_location:"Storage location",part_product_url:"Product URL",part_unit:"Unit",part_cost:"Unit price",part_stock:"Stock",part_reorder_threshold:"Reorder at",part_restock_quantity:"Restock quantity",part_auto_buy:"Auto-create buy task when low",part_restock:"Adjust stock",restock_quantity_label:"Quantity bought",consumes_parts_label:"Consumes parts",shared_parts_other_objects:"Parts from other objects",shared_parts_help:"Several objects can share one stock. Completing this task takes from the owning object.",shared_part_unknown:"Unknown part",parts_load_failed:"Couldn't load this object's parts \u2014 the consumes-parts options are unavailable right now.",adopt_problem_button:"Adopt problem sensors",adopt_problem_title:"Adopt problem sensors",adopt_problem_hint:"Turn HA problem sensors (printer errors, filter warnings, low battery) into maintenance tasks that trigger while the problem is active and clear themselves when it resolves.",adopt_problem_none:"No problem sensors found that aren't already tracked.",adopt_problem_active:"active",adopt_problem_ok:"ok",adopt_problem_new_object:"(new)",adopt_problem_adopt:"Adopt selected",adopt_problem_done:"Adopted {tasks} problem sensor(s)",views_label:"Views",views_none:"\u2014 No view \u2014",views_manage:"Save / manage views",views_dialog_title:"Saved views",views_dialog_hint:"Save the current filters as a named view everyone can reuse.",views_name_placeholder:"View name",views_save_current:"Save current filters",views_none_yet:"No saved views yet.",close:"Close",trigger_hint_now:"The sensor reads {value} right now.",trigger_hint_above:"The task triggers once it rises above {target}.",trigger_hint_below:"It triggers once it falls below {target}.",trigger_hint_counter_delta:"Counts from the current reading ({value}): due at {due} (+{target}), and the count restarts after each completion.",trigger_hint_counter_delta_edit:"Counts usage since the last completion: due after +{target}; the count restarts after each completion.",trigger_hint_counter_abs:"The task becomes due once the sensor reaches {target}.",trigger_hint_runtime:"The task becomes due after {hours} h of accumulated on-time; the counter restarts after each completion.",trigger_hint_state_change:"The task becomes due after {count} state change(s).",trigger_hint_state_change_to:"The task becomes due after {count} change(s) to \u201C{state}\u201D.",trigger_hint_state_now:"Current state: {value}.",adopt_problem_part:"Uses part: {name}",label_filter:"Label",all_labels:"All labels",settings_notify_scope:"Notify only for view",settings_notify_scope_all:"All tasks",settings_notify_scope_hint:"Only tasks matching the selected saved view's label/user filters send reminders. Status, sorting and grouping of the view are ignored here.",card_saved_view:"Saved view",card_saved_view_none:"None",card_saved_view_help:"Applies the view's status, user and label filters on top of the filters above. The view's sorting and grouping are panel display settings and are not applied on the card.",doc_part_none:"No documents linked to this part.",settings_templates_toggle_group:"Enable or disable all templates in this group",setups_button:"Suggested setups",setups_title:"Suggested setups (Beta)",setups_hint:"Devices of supported integrations whose consumable sensors can drive maintenance tasks. Adopting creates the object and wires each task to its sensor \u2014 it triggers when the consumable runs low and resolves itself after replacement.",setups_none:"No supported devices with unwired consumable sensors found.",setups_adopt:"Set up selected",setups_done:"{tasks} sensor-wired tasks created.",complete_parts_used:"Parts used this time",part_delete_confirm:"Delete part '{name}'? Its stock tracking, task links and any open buy reminder will be removed.",baseline_start_value:"Start reading (optional)",baseline_start_help:"Counting starts from this reading. Leave empty to count from the current value; enter the reading at the last service so usage since then already counts.",setups_baseline_hint:"reading at last service (optional)",baseline_start_help_edit:"Leave empty to keep the existing counting. Entering a value re-anchors the counting (e.g. the reading at the last service).",baseline_current_effective:"Currently effective start value: {value}",runtime_on_states:"Active states (comma-separated)",runtime_on_states_help:"States that count as running \u2014 default: on. E.g. mowing, cleaning, printing. With an attribute selected, its values are matched instead.",setups_target_new:"Create new: {name}",schedule_preview_title:"Next dates",schedule_preview_ontime:"Assuming on-time completion.",schedule_preview_ends:"(series ends)",adopt_problem_responsible:"Responsible user for all adopted tasks (optional)",adopt_problem_configure:"Configure",history_auto:"Automatic",battery_fleet_title:"Battery fleet",battery_fleet_none_low:"All batteries OK \u2014 nothing to replace.",battery_fleet_buy_now:"Buy now",battery_fleet_soon:"Needed soon",battery_fleet_soon_hint:"Predicted from the last replacement date \u2014 order ahead.",battery_fleet_mark_all:"Mark all replaced",battery_fleet_mark_one:"Mark this battery replaced",battery_fleet_offline:"offline",battery_fleet_trigger_lost:"This task's sensor trigger was lost \u2014 it will not fire or auto-complete.",battery_fleet_repair:"Repair",battery_fleet_exclude:"Exclude from the fleet",battery_fleet_excluded:"Excluded",battery_fleet_include:"Track again",battery_fleet_all:"All tracked batteries",battery_fleet_all_hint:"Exclude a device here to drop it from the fleet before it ever reports low \u2014 a vacuum that recharges itself, or a phone that warns you on its own.",battery_fleet_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 \u2014 one task tracks all your batteries.",update_banner:"A newer version of Maintenance Supporter is on the server \u2014 reload to update the panel.",update_reload:"Reload"};var Ke="\u20AC",we="en",Qe=(()=>{let a=window;return a.__msLocales||(a.__msLocales={store:{},inflight:{}}),a.__msLocales})(),N=Qe.store;N.en||(N.en=Ge);var Dt=new Set(["de","nl","fr","it","es","pt","pt-br","ru","uk","pl","cs","sv","zh","da","fi","nb","ja","hi","hu","ko","tr"]),Nt="/maintenance_supporter_locales",Z=Qe.inflight;function ke(a){let e=(a||we).toLowerCase();return e.startsWith("pt")&&e.endsWith("br")?"pt-br":e.substring(0,2)}function f(a,e){let t=ke(e);return N[t]?.[a]??N.en[a]??a}function Je(a){let e=ke(a);return e===we||e in N}function Ze(a){let e=ke(a);return e===we||e in N||!Dt.has(e)?Promise.resolve():(e in Z||(Z[e]=fetch(`${Nt}/${e}.json`).then(t=>t.ok?t.json():null).then(t=>{t?N[e]=t:delete Z[e]}).catch(()=>{delete Z[e]})),Z[e])}var Rt=window,Ve=Rt.__msDateTimePrefs??={};function Xe(a){a&&(Ve.date=a.date_format,Ve.time=a.time_format)}function et(a,e){if(a==null)return"\u2014";let t=e||"en";return a<0?`${Math.abs(a)} ${f("d_overdue",t)}`:a===0?f("today",t):`${a} ${f(a===1?"day":"days",t)}`}var Ho=k` .field { display: flex; flex-direction: column; gap: 4px; } .field-label { font-size: 12px; color: var(--secondary-text-color); } .field-input { @@ -211,7 +211,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" font-family: inherit; width: 100%; box-sizing: border-box; } .field-input:focus { outline: none; border-color: var(--primary-color); } -`,et=k` +`,tt=k` :host { --maint-ok-color: var(--success-color, #4caf50); --maint-due-soon-color: var(--warning-color, #ff9800); @@ -1376,44 +1376,44 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" .stat-item .stat-label { font-size: 11px; white-space: normal; text-align: center; line-height: 1.2; } .stat-value { font-size: 20px; } } -`;var v=class extends ${constructor(){super(...arguments);this._config={type:"custom:maintenance-supporter-calendar-card"};this._objects=[];this._stats=null;this._windowDays=30;this._pastDays=0;this._userFilter="";this._objectFilter="";this._unsub=null;this._dataLoaded=!1;this._lastConnection=null}static getConfigElement(){return document.createElement("maintenance-supporter-calendar-card-editor")}static getStubConfig(){return{type:"custom:maintenance-supporter-calendar-card",window_days:30,show_window_chips:!0,show_user_filter:!0}}setConfig(t){this._config={...t},t.past_days&&[30,90].includes(t.past_days)?this._pastDays=t.past_days:t.window_days&&[7,14,30,365].includes(t.window_days)&&(this._windowDays=t.window_days,this._pastDays=0),typeof t.user_filter=="string"&&(this._userFilter=t.user_filter),typeof t.object_filter=="string"&&(this._objectFilter=t.object_filter)}getCardSize(){return 6}get _lang(){return this.hass?.language||"en"}disconnectedCallback(){if(super.disconnectedCallback(),this._unsub){try{this._unsub()}catch{}this._unsub=null}this._dataLoaded=!1,this._lastConnection=null}updated(t){super.updated(t),t.has("hass")&&Ze(this.hass?.locale);let o=this.hass?.language;if(o&&!Qe(o)&&Je(o).then(()=>this.requestUpdate()),t.has("hass")&&this.hass){if(!this._dataLoaded)this._dataLoaded=!0,this._lastConnection=this.hass.connection,this._loadData(),this._subscribe();else if(this.hass.connection!==this._lastConnection){if(this._lastConnection=this.hass.connection,this._unsub){try{this._unsub()}catch{}this._unsub=null}this._subscribe(),this._loadData()}}}async _loadData(){try{let[t,o]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"}),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/statistics"})]);this._objects=t.objects,this._stats=o}catch{}}async _subscribe(){try{let t=await this.hass.connection.subscribeMessage(o=>{let r=o;this._objects=r.objects},{type:"maintenance_supporter/subscribe"});if(!this.isConnected){t();return}this._unsub=t}catch{}}_onEventClick(t){if(t.history_timestamp){this.dispatchEvent(new CustomEvent("ll-custom",{detail:{type:"maintenance-supporter:edit-history",entry_id:t.entry_id,task_id:t.task_id,original_timestamp:t.history_timestamp},bubbles:!0,composed:!0}));return}this.dispatchEvent(new CustomEvent("ll-custom",{detail:{type:"maintenance-supporter:open-task",entry_id:t.entry_id,task_id:t.task_id},bubbles:!0,composed:!0}))}render(){if(!this.hass)return g;let t=this._lang,o=this._config.show_window_chips!==!1,r=this._config.show_user_filter!==!1,n=this._config.title,s=null;this._userFilter&&(s=this._userFilter==="current_user"?this.hass?.user?.id??null:this._userFilter);let l=this._config.show_object_filter!==!1&&this._objects.length>1,c=null;if(this._objectFilter){let i=this._objectFilter.toLowerCase();c=this._objects.find(P=>P.entry_id===this._objectFilter||P.object.name.toLowerCase()===i)?.entry_id??null}let p=c?this._objects.filter(i=>i.entry_id===c):this._objects,u=new Date;u.setHours(0,0,0,0);let d=this._pastDays>0,_=d?Be(p,u,this._pastDays,s):Fe(p,u,this._windowDays,s),h=K(u),b=this._windowDays===365||d,y=b?_.filter(i=>i.events.length>0):_,N=i=>{let R=`cal-status-${i.status}`,P=i.projected?"cal-event-projected":"",se=i.status==="overdue"&&i.days_until_due!=null?` (${Xe(i.days_until_due,t)})`:"",M=i.projected&&i.interval_days?f`${i.interval_unit&&i.interval_unit!=="days"?`${i.interval_days} ${m("unit_"+i.interval_unit,t)}`:m("cal_every_n_days",t).replace("{n}",String(i.interval_days))}`:g,U=i.schedule_type==="sensor_based",ie=U?f``:f``,le=U&&i.prediction_confidence&&i.status!=="triggered"&&!i.projected?f` - ${m("cal_predicted",t)} · ${m(`cal_confidence_${i.prediction_confidence}`,t)} - `:g,ot=this._stats?.budget?.currency_symbol||Ve,rt=i.history_type?m(i.history_type,t):m(i.status,t);return f` -
typeof r=="string"&&r!=="");this._objectFilter=o.length===1?o[0]:"",this._configuredObjects=o.length>1?o:[]}}getCardSize(){return 6}get _lang(){return this.hass?.language||"en"}disconnectedCallback(){if(super.disconnectedCallback(),this._unsub){try{this._unsub()}catch{}this._unsub=null}this._dataLoaded=!1,this._lastConnection=null}updated(t){super.updated(t),t.has("hass")&&Xe(this.hass?.locale);let o=this.hass?.language;if(o&&!Je(o)&&Ze(o).then(()=>this.requestUpdate()),t.has("hass")&&this.hass){if(!this._dataLoaded)this._dataLoaded=!0,this._lastConnection=this.hass.connection,this._loadData(),this._subscribe();else if(this.hass.connection!==this._lastConnection){if(this._lastConnection=this.hass.connection,this._unsub){try{this._unsub()}catch{}this._unsub=null}this._subscribe(),this._loadData()}}}async _loadData(){try{let[t,o]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"}),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/statistics"})]);this._objects=t.objects,this._stats=o}catch{}}async _subscribe(){try{let t=await this.hass.connection.subscribeMessage(o=>{let r=o;this._objects=r.objects},{type:"maintenance_supporter/subscribe"});if(!this.isConnected){t();return}this._unsub=t}catch{}}_onEventClick(t){if(t.history_timestamp){this.dispatchEvent(new CustomEvent("ll-custom",{detail:{type:"maintenance-supporter:edit-history",entry_id:t.entry_id,task_id:t.task_id,original_timestamp:t.history_timestamp},bubbles:!0,composed:!0}));return}this.dispatchEvent(new CustomEvent("ll-custom",{detail:{type:"maintenance-supporter:open-task",entry_id:t.entry_id,task_id:t.task_id},bubbles:!0,composed:!0}))}render(){if(!this.hass)return g;let t=this._lang,o=this._config.show_window_chips!==!1,r=this._config.show_user_filter!==!1,n=this._config.title,s=null;this._userFilter&&(s=this._userFilter==="current_user"?this.hass?.user?.id??null:this._userFilter);let l=i=>{let j=i.toLowerCase();return this._objects.find(P=>P.entry_id===i||P.object.name.toLowerCase()===j)?.entry_id??null},d=new Set(this._configuredObjects.map(l).filter(i=>i!==null)),p=d.size?this._objects.filter(i=>d.has(i.entry_id)):this._objects,u=this._config.show_object_filter!==!1&&p.length>1,c=this._objectFilter?l(this._objectFilter):null,_=c&&p.some(i=>i.entry_id===c)?p.filter(i=>i.entry_id===c):p,h=new Date;h.setHours(0,0,0,0);let b=this._pastDays>0,y=b?We(_,h,this._pastDays,s):Be(_,h,this._windowDays,s),R=J(h),M=this._windowDays===365||b,U=M?y.filter(i=>i.events.length>0):y,rt=i=>{let j=`cal-status-${i.status}`,X=i.projected?"cal-event-projected":"",P=i.status==="overdue"&&i.days_until_due!=null?` (${et(i.days_until_due,t)})`:"",H=i.projected&&i.interval_days?m`${i.interval_unit&&i.interval_unit!=="days"?`${i.interval_days} ${f("unit_"+i.interval_unit,t)}`:f("cal_every_n_days",t).replace("{n}",String(i.interval_days))}`:g,q=i.schedule_type==="sensor_based",le=q?m``:m``,ce=q&&i.prediction_confidence&&i.status!=="triggered"&&!i.projected?m` + ${f("cal_predicted",t)} · ${f(`cal_confidence_${i.prediction_confidence}`,t)} + `:g,nt=this._stats?.budget?.currency_symbol||Ke,st=i.history_type?f(i.history_type,t):f(i.status,t);return m` +
this._onEventClick(i)}> - ${ie} - ${rt} + ${le} + ${st}
-
${i.object_name} · ${i.task_name}${se}
- ${le} - ${M} +
${i.object_name} · ${i.task_name}${P}
+ ${ce} + ${H}
- ${i.avg_cost!=null&&i.avg_cost>0?f`${i.avg_cost.toFixed(0)} ${ot}`:g} + ${i.avg_cost!=null&&i.avg_cost>0?m`${i.avg_cost.toFixed(0)} ${nt}`:g}
- `},J=i=>{let[R,P,se]=i.date.split("-").map(Number),M=new Date(R,P-1,se),U=i.date===h,ie=M.toLocaleDateString(t,{weekday:"short"}),le=M.toLocaleDateString(t,{month:"long"});return f` + `},at=i=>{let[j,X,P]=i.date.split("-").map(Number),H=new Date(j,X-1,P),q=i.date===R,le=H.toLocaleDateString(t,{weekday:"short"}),ce=H.toLocaleDateString(t,{month:"long"});return m`
-
- ${ie} - ${M.getDate()} +
+ ${le} + ${H.getDate()}
- ${le} - ${U?f`${m("today",t)}`:g} + ${ce} + ${q?m`${f("today",t)}`:g}
- ${i.events.length===0?f`
${m("cal_no_events",t)}
`:i.events.map(N)} + ${i.events.length===0?m`
${f("cal_no_events",t)}
`:i.events.map(rt)}
- `};return f` + `};return m` - ${o||r?f` + ${o||r?m`
- ${o?f` -
- ${[30,90].map(i=>f` + ${o?m` +
+ ${[30,90].map(i=>m`
-
- ${[7,14,30,365].map(i=>f` +
+ ${[7,14,30,365].map(i=>m`
`:g} - ${r?f` + ${r?m` `:g} - ${l?f` + ${u?m` `:g}
`:g}
- ${y.length===0&&b?f`
${m("cal_no_events",t)}
`:y.map(J)} + ${U.length===0&&M?m`
${f("cal_no_events",t)}
`:U.map(at)}
- `}};v.styles=[et,We,k` + `}};v.styles=[tt,Ye,k` :host { display: block; } ha-card { padding: 0; overflow: hidden; } - `],x([V({attribute:!1})],v.prototype,"hass",2),x([w()],v.prototype,"_config",2),x([w()],v.prototype,"_objects",2),x([w()],v.prototype,"_stats",2),x([w()],v.prototype,"_windowDays",2),x([w()],v.prototype,"_pastDays",2),x([w()],v.prototype,"_userFilter",2),x([w()],v.prototype,"_objectFilter",2),x([w()],v.prototype,"_unsub",2);var Dt=[{value:7,label:"Week (7 days)"},{value:14,label:"Fortnight (14 days)"},{value:30,label:"Month (30 days, default)"},{value:365,label:"Year (365 days, empty days collapsed)"}],O=class extends ${constructor(){super(...arguments);this._config={type:"custom:maintenance-supporter-calendar-card"}}setConfig(t){this._config={...t}}_valueChanged(t,o){let r={...this._config,[t]:o};t==="show_window_chips"&&o===!0&&delete r.show_window_chips,t==="show_user_filter"&&o===!0&&delete r.show_user_filter,t==="show_object_filter"&&o===!0&&delete r.show_object_filter,t==="title"&&(!o||typeof o=="string"&&o.trim()==="")&&delete r.title,t==="user_filter"&&o===""&&delete r.user_filter,this._config=r,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:r},bubbles:!0,composed:!0}))}render(){let t=this._config.window_days??30,o=this._config.show_window_chips!==!1,r=this._config.show_user_filter!==!1,n=this._config.user_filter??"",s=this._config.title??"";return f` + `],x([Q({attribute:!1})],v.prototype,"hass",2),x([w()],v.prototype,"_config",2),x([w()],v.prototype,"_objects",2),x([w()],v.prototype,"_stats",2),x([w()],v.prototype,"_windowDays",2),x([w()],v.prototype,"_pastDays",2),x([w()],v.prototype,"_userFilter",2),x([w()],v.prototype,"_objectFilter",2),x([w()],v.prototype,"_unsub",2);var Pt=[{value:7,label:"Week (7 days)"},{value:14,label:"Fortnight (14 days)"},{value:30,label:"Month (30 days, default)"},{value:365,label:"Year (365 days, empty days collapsed)"}],L=class extends ${constructor(){super(...arguments);this._config={type:"custom:maintenance-supporter-calendar-card"}}setConfig(t){this._config={...t}}_valueChanged(t,o){let r={...this._config,[t]:o};t==="show_window_chips"&&o===!0&&delete r.show_window_chips,t==="show_user_filter"&&o===!0&&delete r.show_user_filter,t==="show_object_filter"&&o===!0&&delete r.show_object_filter,t==="title"&&(!o||typeof o=="string"&&o.trim()==="")&&delete r.title,t==="user_filter"&&o===""&&delete r.user_filter,this._config=r,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:r},bubbles:!0,composed:!0}))}render(){let t=this._config.window_days??30,o=this._config.show_window_chips!==!1,r=this._config.show_user_filter!==!1,n=this._config.user_filter??"",s=this._config.title??"";return m`
@@ -1472,7 +1472,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" id="window" @change=${l=>this._valueChanged("window_days",Number(l.target.value))} > - ${Dt.map(l=>f``)} + ${Pt.map(l=>m``)}
@@ -1519,10 +1519,11 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" />
- 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.
- `}};O.styles=k` + `}};L.styles=k` :host { display: block; padding: 8px 0; } .editor { display: flex; flex-direction: column; gap: 12px; } .row { display: flex; flex-direction: column; gap: 4px; } @@ -1546,7 +1547,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" font-size: 12px; color: var(--secondary-text-color, #666); } - `,x([V({attribute:!1})],O.prototype,"hass",2),x([w()],O.prototype,"_config",2);customElements.get("maintenance-supporter-calendar-card")||customElements.define("maintenance-supporter-calendar-card",v);customElements.get("maintenance-supporter-calendar-card-editor")||customElements.define("maintenance-supporter-calendar-card-editor",O);var ne=window;ne.customCards=ne.customCards||[];var tt="maintenance-supporter-calendar-card",Nt=ne.customCards.some(a=>a.type===tt);Nt||ne.customCards.push({type:tt,name:"Maintenance Supporter \u2014 Calendar",description:"Rolling calendar of maintenance tasks with 7/14/30/365 day windows, source icons, and prediction-confidence pills.",preview:!0});export{v as MaintenanceCalendarCard}; + `,x([Q({attribute:!1})],L.prototype,"hass",2),x([w()],L.prototype,"_config",2);customElements.get("maintenance-supporter-calendar-card")||customElements.define("maintenance-supporter-calendar-card",v);customElements.get("maintenance-supporter-calendar-card-editor")||customElements.define("maintenance-supporter-calendar-card-editor",L);var ie=window;ie.customCards=ie.customCards||[];var ot="maintenance-supporter-calendar-card",zt=ie.customCards.some(a=>a.type===ot);zt||ie.customCards.push({type:ot,name:"Maintenance Supporter \u2014 Calendar",description:"Rolling calendar of maintenance tasks with 7/14/30/365 day windows, source icons, and prediction-confidence pills.",preview:!0});export{v as MaintenanceCalendarCard}; /*! Bundled license information: @lit/reactive-element/css-tag.js: diff --git a/custom_components/maintenance_supporter/frontend/maintenance-card.js b/custom_components/maintenance_supporter/frontend/maintenance-card.js index ad2ca00..37c4f35 100644 --- a/custom_components/maintenance_supporter/frontend/maintenance-card.js +++ b/custom_components/maintenance_supporter/frontend/maintenance-card.js @@ -1,9 +1,9 @@ -/*! maintenance_supporter frontend 2.44.1 */ -var _t=Object.defineProperty;var Ci=Object.getOwnPropertyDescriptor;var $=(a,s,e)=>()=>{if(e)throw e[0];try{return a&&(s=a(a=0)),s}catch(t){throw e=[t],t}};var Li=(a,s)=>{for(var e in s)_t(a,e,{get:s[e],enumerable:!0})};var l=(a,s,e,t)=>{for(var i=t>1?void 0:t?Ci(s,e):s,n=a.length-1,c;n>=0;n--)(c=a[n])&&(i=(t?c(s,e,i):c(i))||i);return t&&i&&_t(s,e,i),i};var Ee,Se,Fe,gt,pe,mt,E,ft,Ue,Ve=$(()=>{Ee=globalThis,Se=Ee.ShadowRoot&&(Ee.ShadyCSS===void 0||Ee.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Fe=Symbol(),gt=new WeakMap,pe=class{constructor(s,e,t){if(this._$cssResult$=!0,t!==Fe)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=s,this.t=e}get styleSheet(){let s=this.o,e=this.t;if(Se&&s===void 0){let t=e!==void 0&&e.length===1;t&&(s=gt.get(e)),s===void 0&&((this.o=s=new CSSStyleSheet).replaceSync(this.cssText),t&>.set(e,s))}return s}toString(){return this.cssText}},mt=a=>new pe(typeof a=="string"?a:a+"",void 0,Fe),E=(a,...s)=>{let e=a.length===1?a[0]:s.reduce((t,i,n)=>t+(c=>{if(c._$cssResult$===!0)return c.cssText;if(typeof c=="number")return c;throw Error("Value passed to 'css' function must be a 'css' function result: "+c+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+a[n+1],a[0]);return new pe(e,a,Fe)},ft=(a,s)=>{if(Se)a.adoptedStyleSheets=s.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let e of s){let t=document.createElement("style"),i=Ee.litNonce;i!==void 0&&t.setAttribute("nonce",i),t.textContent=e.cssText,a.appendChild(t)}},Ue=Se?a=>a:a=>a instanceof CSSStyleSheet?(s=>{let e="";for(let t of s.cssRules)e+=t.cssText;return mt(e)})(a):a});var Ii,Pi,Ri,ji,Hi,Ni,Ae,vt,qi,zi,he,ue,Te,bt,V,_e=$(()=>{Ve();Ve();({is:Ii,defineProperty:Pi,getOwnPropertyDescriptor:Ri,getOwnPropertyNames:ji,getOwnPropertySymbols:Hi,getPrototypeOf:Ni}=Object),Ae=globalThis,vt=Ae.trustedTypes,qi=vt?vt.emptyScript:"",zi=Ae.reactiveElementPolyfillSupport,he=(a,s)=>a,ue={toAttribute(a,s){switch(s){case Boolean:a=a?qi:null;break;case Object:case Array:a=a==null?a:JSON.stringify(a)}return a},fromAttribute(a,s){let e=a;switch(s){case Boolean:e=a!==null;break;case Number:e=a===null?null:Number(a);break;case Object:case Array:try{e=JSON.parse(a)}catch{e=null}}return e}},Te=(a,s)=>!Ii(a,s),bt={attribute:!0,type:String,converter:ue,reflect:!1,useDefault:!1,hasChanged:Te};Symbol.metadata??=Symbol("metadata"),Ae.litPropertyMetadata??=new WeakMap;V=class extends HTMLElement{static addInitializer(s){this._$Ei(),(this.l??=[]).push(s)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(s,e=bt){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(s)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(s,e),!e.noAccessor){let t=Symbol(),i=this.getPropertyDescriptor(s,t,e);i!==void 0&&Pi(this.prototype,s,i)}}static getPropertyDescriptor(s,e,t){let{get:i,set:n}=Ri(this.prototype,s)??{get(){return this[e]},set(c){this[e]=c}};return{get:i,set(c){let d=i?.call(this);n?.call(this,c),this.requestUpdate(s,d,t)},configurable:!0,enumerable:!0}}static getPropertyOptions(s){return this.elementProperties.get(s)??bt}static _$Ei(){if(this.hasOwnProperty(he("elementProperties")))return;let s=Ni(this);s.finalize(),s.l!==void 0&&(this.l=[...s.l]),this.elementProperties=new Map(s.elementProperties)}static finalize(){if(this.hasOwnProperty(he("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(he("properties"))){let e=this.properties,t=[...ji(e),...Hi(e)];for(let i of t)this.createProperty(i,e[i])}let s=this[Symbol.metadata];if(s!==null){let e=litPropertyMetadata.get(s);if(e!==void 0)for(let[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let i=this._$Eu(e,t);i!==void 0&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(s){let e=[];if(Array.isArray(s)){let t=new Set(s.flat(1/0).reverse());for(let i of t)e.unshift(Ue(i))}else s!==void 0&&e.push(Ue(s));return e}static _$Eu(s,e){let t=e.attribute;return t===!1?void 0:typeof t=="string"?t:typeof s=="string"?s.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(s=>this.enableUpdating=s),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(s=>s(this))}addController(s){(this._$EO??=new Set).add(s),this.renderRoot!==void 0&&this.isConnected&&s.hostConnected?.()}removeController(s){this._$EO?.delete(s)}_$E_(){let s=new Map,e=this.constructor.elementProperties;for(let t of e.keys())this.hasOwnProperty(t)&&(s.set(t,this[t]),delete this[t]);s.size>0&&(this._$Ep=s)}createRenderRoot(){let s=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return ft(s,this.constructor.elementStyles),s}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(s=>s.hostConnected?.())}enableUpdating(s){}disconnectedCallback(){this._$EO?.forEach(s=>s.hostDisconnected?.())}attributeChangedCallback(s,e,t){this._$AK(s,t)}_$ET(s,e){let t=this.constructor.elementProperties.get(s),i=this.constructor._$Eu(s,t);if(i!==void 0&&t.reflect===!0){let n=(t.converter?.toAttribute!==void 0?t.converter:ue).toAttribute(e,t.type);this._$Em=s,n==null?this.removeAttribute(i):this.setAttribute(i,n),this._$Em=null}}_$AK(s,e){let t=this.constructor,i=t._$Eh.get(s);if(i!==void 0&&this._$Em!==i){let n=t.getPropertyOptions(i),c=typeof n.converter=="function"?{fromAttribute:n.converter}:n.converter?.fromAttribute!==void 0?n.converter:ue;this._$Em=i;let d=c.fromAttribute(e,n.type);this[i]=d??this._$Ej?.get(i)??d,this._$Em=null}}requestUpdate(s,e,t,i=!1,n){if(s!==void 0){let c=this.constructor;if(i===!1&&(n=this[s]),t??=c.getPropertyOptions(s),!((t.hasChanged??Te)(n,e)||t.useDefault&&t.reflect&&n===this._$Ej?.get(s)&&!this.hasAttribute(c._$Eu(s,t))))return;this.C(s,e,t)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(s,e,{useDefault:t,reflect:i,wrapped:n},c){t&&!(this._$Ej??=new Map).has(s)&&(this._$Ej.set(s,c??e??this[s]),n!==!0||c!==void 0)||(this._$AL.has(s)||(this.hasUpdated||t||(e=void 0),this._$AL.set(s,e)),i===!0&&this._$Em!==s&&(this._$Eq??=new Set).add(s))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}let s=this.scheduleUpdate();return s!=null&&await s,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(let[i,n]of this._$Ep)this[i]=n;this._$Ep=void 0}let t=this.constructor.elementProperties;if(t.size>0)for(let[i,n]of t){let{wrapped:c}=n,d=this[i];c!==!0||this._$AL.has(i)||d===void 0||this.C(i,void 0,n,d)}}let s=!1,e=this._$AL;try{s=this.shouldUpdate(e),s?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(t){throw s=!1,this._$EM(),t}s&&this._$AE(e)}willUpdate(s){}_$AE(s){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(s)),this.updated(s)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(s){return!0}update(s){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(s){}firstUpdated(s){}};V.elementStyles=[],V.shadowRootOptions={mode:"open"},V[he("elementProperties")]=new Map,V[he("finalized")]=new Map,zi?.({ReactiveElement:V}),(Ae.reactiveElementVersions??=[]).push("2.1.2")});function Lt(a,s){if(!Ze(a)||!a.hasOwnProperty("raw"))throw Error("invalid template strings array");return xt!==void 0?xt.createHTML(s):s}function ae(a,s,e=a,t){if(s===te)return s;let i=t!==void 0?e._$Co?.[t]:e._$Cl,n=fe(s)?void 0:s._$litDirective$;return i?.constructor!==n&&(i?._$AO?.(!1),n===void 0?i=void 0:(i=new n(a),i._$AT(a,e,t)),t!==void 0?(e._$Co??=[])[t]=i:e._$Cl=i),i!==void 0&&(s=ae(a,i._$AS(a,s.values),i,t)),s}var Qe,yt,Ce,xt,At,J,Tt,Mi,ee,me,fe,Ze,Oi,Be,ge,$t,wt,Z,kt,Et,Ct,Xe,o,oe,js,te,h,St,X,Di,ve,We,be,ne,Ge,Ke,Ye,Je,Fi,It,Le=$(()=>{Qe=globalThis,yt=a=>a,Ce=Qe.trustedTypes,xt=Ce?Ce.createPolicy("lit-html",{createHTML:a=>a}):void 0,At="$lit$",J=`lit$${Math.random().toFixed(9).slice(2)}$`,Tt="?"+J,Mi=`<${Tt}>`,ee=document,me=()=>ee.createComment(""),fe=a=>a===null||typeof a!="object"&&typeof a!="function",Ze=Array.isArray,Oi=a=>Ze(a)||typeof a?.[Symbol.iterator]=="function",Be=`[ -\f\r]`,ge=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,$t=/-->/g,wt=/>/g,Z=RegExp(`>|${Be}(?:([^\\s"'>=/]+)(${Be}*=${Be}*(?:[^ -\f\r"'\`<>=]|("|')|))|$)`,"g"),kt=/'/g,Et=/"/g,Ct=/^(?:script|style|textarea|title)$/i,Xe=a=>(s,...e)=>({_$litType$:a,strings:s,values:e}),o=Xe(1),oe=Xe(2),js=Xe(3),te=Symbol.for("lit-noChange"),h=Symbol.for("lit-nothing"),St=new WeakMap,X=ee.createTreeWalker(ee,129);Di=(a,s)=>{let e=a.length-1,t=[],i,n=s===2?"":s===3?"":"",c=ge;for(let d=0;d"?(c=i??ge,f=-1):v[1]===void 0?f=-2:(f=c.lastIndex-v[2].length,_=v[1],c=v[3]===void 0?Z:v[3]==='"'?Et:kt):c===Et||c===kt?c=Z:c===$t||c===wt?c=ge:(c=Z,i=void 0);let g=c===Z&&a[d+1].startsWith("/>")?" ":"";n+=c===ge?u+Mi:f>=0?(t.push(_),u.slice(0,f)+At+u.slice(f)+J+g):u+J+(f===-2?d:g)}return[Lt(a,n+(a[e]||"")+(s===2?"":s===3?"":"")),t]},ve=class a{constructor({strings:s,_$litType$:e},t){let i;this.parts=[];let n=0,c=0,d=s.length-1,u=this.parts,[_,v]=Di(s,e);if(this.el=a.createElement(_,t),X.currentNode=this.el.content,e===2||e===3){let f=this.el.content.firstChild;f.replaceWith(...f.childNodes)}for(;(i=X.nextNode())!==null&&u.length0){i.textContent=Ce?Ce.emptyScript:"";for(let g=0;g2||t[0]!==""||t[1]!==""?(this._$AH=Array(t.length-1).fill(new String),this.strings=t):this._$AH=h}_$AI(s,e=this,t,i){let n=this.strings,c=!1;if(n===void 0)s=ae(this,s,e,0),c=!fe(s)||s!==this._$AH&&s!==te,c&&(this._$AH=s);else{let d=s,u,_;for(s=n[0],u=0;u{let t=e?.renderBefore??s,i=t._$litPart$;if(i===void 0){let n=e?.renderBefore??null;t._$litPart$=i=new be(s.insertBefore(me(),n),n,void 0,e??{})}return i._$AI(a),i}});var et,k,Ui,Pt=$(()=>{_e();_e();Le();Le();et=globalThis,k=class extends V{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){let s=super.createRenderRoot();return this.renderOptions.renderBefore??=s.firstChild,s}update(s){let e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(s),this._$Do=It(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return te}};k._$litElement$=!0,k.finalized=!0,et.litElementHydrateSupport?.({LitElement:k});Ui=et.litElementPolyfillSupport;Ui?.({LitElement:k});(et.litElementVersions??=[]).push("4.2.2")});var Rt=$(()=>{});var I=$(()=>{_e();Le();Pt();Rt()});var jt=$(()=>{});function y(a){return(s,e)=>typeof e=="object"?Bi(a,s,e):((t,i,n)=>{let c=i.hasOwnProperty(n);return i.constructor.createProperty(n,t),c?Object.getOwnPropertyDescriptor(i,n):void 0})(a,s,e)}var Vi,Bi,tt=$(()=>{_e();Vi={attribute:!0,type:String,converter:ue,reflect:!1,hasChanged:Te},Bi=(a=Vi,s,e)=>{let{kind:t,metadata:i}=e,n=globalThis.litPropertyMetadata.get(i);if(n===void 0&&globalThis.litPropertyMetadata.set(i,n=new Map),t==="setter"&&((a=Object.create(a)).wrapped=!0),n.set(e.name,a),t==="accessor"){let{name:c}=e;return{set(d){let u=s.get.call(this);s.set.call(this,d),this.requestUpdate(c,u,a,!0,d)},init(d){return d!==void 0&&this.C(c,void 0,a,d),d}}}if(t==="setter"){let{name:c}=e;return function(d){let u=this[c];s.call(this,d),this.requestUpdate(c,u,a,!0,d)}}throw Error("Unsupported decorator location: "+t)}});function p(a){return y({...a,state:!0,attribute:!1})}var Ht=$(()=>{tt();});var Nt=$(()=>{});var le=$(()=>{});var qt=$(()=>{le();});var zt=$(()=>{le();});var Mt=$(()=>{le();});var Ot=$(()=>{le();});var Dt=$(()=>{le();});var M=$(()=>{jt();tt();Ht();Nt();qt();zt();Mt();Ot();Dt()});var Ut,Ft=$(()=>{Ut={maintenance:"Maintenance",objects:"Objects",tasks:"Tasks",overdue:"Overdue",due_soon:"Due Soon",triggered:"Triggered",trigger_replaced:"Trigger replaced",ok:"OK",all:"All",new_object:"+ New Object",templates_from:"From template",templates_title:"Start from a template",templates_task_count:"{n} tasks",template_created:"Created from template",onboard_hint:"Add your first object to start tracking maintenance.",edit:"Edit",duplicate:"Duplicate",task_duplicated:"Task duplicated",object_duplicated:"Object duplicated",delete:"Delete",add_task:"+ Add Task",complete:"Complete",completed:"Completed",skip:"Skip",skipped:"Skipped",missed:"Missed",reset:"Reset",snooze:"Snooze",snoozed:"Snoozed",cancel:"Cancel",bulk_select:"Select",bulk_select_all:"Select all",bulk_n_selected:"{n} selected",bulk_completed:"{n} tasks completed",bulk_archived:"{n} tasks archived",completing:"Completing\u2026",interval:"Interval",warning:"Warning",last_performed:"Last performed",next_due:"Next due",days_until_due:"Days until due",avg_duration:"Avg duration",trigger:"Trigger",trigger_type:"Trigger type",threshold_above:"Upper limit",threshold_below:"Lower limit",threshold:"Threshold",counter:"Counter",state_change:"State change",runtime:"Runtime",runtime_hours:"Target runtime (hours)",target_value:"Target value",baseline:"Baseline",target_changes:"Target changes",for_minutes:"For (minutes)",time_based:"Time-based",sensor_based:"Sensor-based",manual:"Manual",one_time:"One-time",weekdays:"Weekdays",nth_weekday:"Nth weekday of month",day_of_month:"Day of month",recurrence_on_days:"Repeat on",recurrence_occurrence:"Occurrence",recurrence_weekday:"Weekday",recurrence_day:"Day of month (1\u201331)",recurrence_last_day:"Last day of the month",recurrence_business_day:"Business days only (roll back from weekend)",recurrence_offset:"Offset (days, \xB1)",recurrence_offset_help:"Shift the date by \xB1N days, e.g. -2 = two days before.",last_day_month:"Last day of month",last_business_day_month:"Last business day",ord_1:"1st",ord_2:"2nd",ord_3:"3rd",ord_4:"4th",ord_5:"5th",ord_last:"Last",day_word:"Day",interval_value:"Interval",interval_unit:"Unit",unit_days:"Days",unit_weeks:"Weeks",unit_months:"Months",unit_years:"Years",due_date:"Due date",cleaning:"Cleaning",inspection:"Inspection",replacement:"Replacement",calibration:"Calibration",service:"Service",reading:"Reading",custom:"Custom",history:"History",cost:"Cost",report_button:"Report",report_title:"Maintenance report",report_generated:"Generated",report_times_done:"Done",report_total_cost:"Total cost",report_every:"every {n} {unit}",report_notes:"Notes",report_col_type:"Type",report_col_status:"Status",report_col_schedule:"Schedule",duration:"Duration",both:"Both",trigger_val:"Trigger value",complete_title:"Complete: ",checklist:"Checklist",require_on_completion:"Require on completion",checklist_steps_optional:"Checklist steps (optional)",checklist_placeholder:`Clean filter +/*! maintenance_supporter frontend 2.46.0 */ +var _t=Object.defineProperty;var Ci=Object.getOwnPropertyDescriptor;var w=(a,s,e)=>()=>{if(e)throw e[0];try{return a&&(s=a(a=0)),s}catch(t){throw e=[t],t}};var Li=(a,s)=>{for(var e in s)_t(a,e,{get:s[e],enumerable:!0})};var l=(a,s,e,t)=>{for(var i=t>1?void 0:t?Ci(s,e):s,n=a.length-1,c;n>=0;n--)(c=a[n])&&(i=(t?c(s,e,i):c(i))||i);return t&&i&&_t(s,e,i),i};var Ee,Se,Fe,gt,pe,mt,E,ft,Ue,Ve=w(()=>{Ee=globalThis,Se=Ee.ShadowRoot&&(Ee.ShadyCSS===void 0||Ee.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Fe=Symbol(),gt=new WeakMap,pe=class{constructor(s,e,t){if(this._$cssResult$=!0,t!==Fe)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=s,this.t=e}get styleSheet(){let s=this.o,e=this.t;if(Se&&s===void 0){let t=e!==void 0&&e.length===1;t&&(s=gt.get(e)),s===void 0&&((this.o=s=new CSSStyleSheet).replaceSync(this.cssText),t&>.set(e,s))}return s}toString(){return this.cssText}},mt=a=>new pe(typeof a=="string"?a:a+"",void 0,Fe),E=(a,...s)=>{let e=a.length===1?a[0]:s.reduce((t,i,n)=>t+(c=>{if(c._$cssResult$===!0)return c.cssText;if(typeof c=="number")return c;throw Error("Value passed to 'css' function must be a 'css' function result: "+c+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+a[n+1],a[0]);return new pe(e,a,Fe)},ft=(a,s)=>{if(Se)a.adoptedStyleSheets=s.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let e of s){let t=document.createElement("style"),i=Ee.litNonce;i!==void 0&&t.setAttribute("nonce",i),t.textContent=e.cssText,a.appendChild(t)}},Ue=Se?a=>a:a=>a instanceof CSSStyleSheet?(s=>{let e="";for(let t of s.cssRules)e+=t.cssText;return mt(e)})(a):a});var Ii,Pi,Ri,ji,Hi,Ni,Ae,vt,qi,zi,he,ue,Te,bt,V,_e=w(()=>{Ve();Ve();({is:Ii,defineProperty:Pi,getOwnPropertyDescriptor:Ri,getOwnPropertyNames:ji,getOwnPropertySymbols:Hi,getPrototypeOf:Ni}=Object),Ae=globalThis,vt=Ae.trustedTypes,qi=vt?vt.emptyScript:"",zi=Ae.reactiveElementPolyfillSupport,he=(a,s)=>a,ue={toAttribute(a,s){switch(s){case Boolean:a=a?qi:null;break;case Object:case Array:a=a==null?a:JSON.stringify(a)}return a},fromAttribute(a,s){let e=a;switch(s){case Boolean:e=a!==null;break;case Number:e=a===null?null:Number(a);break;case Object:case Array:try{e=JSON.parse(a)}catch{e=null}}return e}},Te=(a,s)=>!Ii(a,s),bt={attribute:!0,type:String,converter:ue,reflect:!1,useDefault:!1,hasChanged:Te};Symbol.metadata??=Symbol("metadata"),Ae.litPropertyMetadata??=new WeakMap;V=class extends HTMLElement{static addInitializer(s){this._$Ei(),(this.l??=[]).push(s)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(s,e=bt){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(s)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(s,e),!e.noAccessor){let t=Symbol(),i=this.getPropertyDescriptor(s,t,e);i!==void 0&&Pi(this.prototype,s,i)}}static getPropertyDescriptor(s,e,t){let{get:i,set:n}=Ri(this.prototype,s)??{get(){return this[e]},set(c){this[e]=c}};return{get:i,set(c){let d=i?.call(this);n?.call(this,c),this.requestUpdate(s,d,t)},configurable:!0,enumerable:!0}}static getPropertyOptions(s){return this.elementProperties.get(s)??bt}static _$Ei(){if(this.hasOwnProperty(he("elementProperties")))return;let s=Ni(this);s.finalize(),s.l!==void 0&&(this.l=[...s.l]),this.elementProperties=new Map(s.elementProperties)}static finalize(){if(this.hasOwnProperty(he("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(he("properties"))){let e=this.properties,t=[...ji(e),...Hi(e)];for(let i of t)this.createProperty(i,e[i])}let s=this[Symbol.metadata];if(s!==null){let e=litPropertyMetadata.get(s);if(e!==void 0)for(let[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let i=this._$Eu(e,t);i!==void 0&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(s){let e=[];if(Array.isArray(s)){let t=new Set(s.flat(1/0).reverse());for(let i of t)e.unshift(Ue(i))}else s!==void 0&&e.push(Ue(s));return e}static _$Eu(s,e){let t=e.attribute;return t===!1?void 0:typeof t=="string"?t:typeof s=="string"?s.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(s=>this.enableUpdating=s),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(s=>s(this))}addController(s){(this._$EO??=new Set).add(s),this.renderRoot!==void 0&&this.isConnected&&s.hostConnected?.()}removeController(s){this._$EO?.delete(s)}_$E_(){let s=new Map,e=this.constructor.elementProperties;for(let t of e.keys())this.hasOwnProperty(t)&&(s.set(t,this[t]),delete this[t]);s.size>0&&(this._$Ep=s)}createRenderRoot(){let s=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return ft(s,this.constructor.elementStyles),s}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(s=>s.hostConnected?.())}enableUpdating(s){}disconnectedCallback(){this._$EO?.forEach(s=>s.hostDisconnected?.())}attributeChangedCallback(s,e,t){this._$AK(s,t)}_$ET(s,e){let t=this.constructor.elementProperties.get(s),i=this.constructor._$Eu(s,t);if(i!==void 0&&t.reflect===!0){let n=(t.converter?.toAttribute!==void 0?t.converter:ue).toAttribute(e,t.type);this._$Em=s,n==null?this.removeAttribute(i):this.setAttribute(i,n),this._$Em=null}}_$AK(s,e){let t=this.constructor,i=t._$Eh.get(s);if(i!==void 0&&this._$Em!==i){let n=t.getPropertyOptions(i),c=typeof n.converter=="function"?{fromAttribute:n.converter}:n.converter?.fromAttribute!==void 0?n.converter:ue;this._$Em=i;let d=c.fromAttribute(e,n.type);this[i]=d??this._$Ej?.get(i)??d,this._$Em=null}}requestUpdate(s,e,t,i=!1,n){if(s!==void 0){let c=this.constructor;if(i===!1&&(n=this[s]),t??=c.getPropertyOptions(s),!((t.hasChanged??Te)(n,e)||t.useDefault&&t.reflect&&n===this._$Ej?.get(s)&&!this.hasAttribute(c._$Eu(s,t))))return;this.C(s,e,t)}this.isUpdatePending===!1&&(this._$ES=this._$EP())}C(s,e,{useDefault:t,reflect:i,wrapped:n},c){t&&!(this._$Ej??=new Map).has(s)&&(this._$Ej.set(s,c??e??this[s]),n!==!0||c!==void 0)||(this._$AL.has(s)||(this.hasUpdated||t||(e=void 0),this._$AL.set(s,e)),i===!0&&this._$Em!==s&&(this._$Eq??=new Set).add(s))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(e){Promise.reject(e)}let s=this.scheduleUpdate();return s!=null&&await s,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(let[i,n]of this._$Ep)this[i]=n;this._$Ep=void 0}let t=this.constructor.elementProperties;if(t.size>0)for(let[i,n]of t){let{wrapped:c}=n,d=this[i];c!==!0||this._$AL.has(i)||d===void 0||this.C(i,void 0,n,d)}}let s=!1,e=this._$AL;try{s=this.shouldUpdate(e),s?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(t){throw s=!1,this._$EM(),t}s&&this._$AE(e)}willUpdate(s){}_$AE(s){this._$EO?.forEach(e=>e.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(s)),this.updated(s)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(s){return!0}update(s){this._$Eq&&=this._$Eq.forEach(e=>this._$ET(e,this[e])),this._$EM()}updated(s){}firstUpdated(s){}};V.elementStyles=[],V.shadowRootOptions={mode:"open"},V[he("elementProperties")]=new Map,V[he("finalized")]=new Map,zi?.({ReactiveElement:V}),(Ae.reactiveElementVersions??=[]).push("2.1.2")});function Lt(a,s){if(!Ze(a)||!a.hasOwnProperty("raw"))throw Error("invalid template strings array");return xt!==void 0?xt.createHTML(s):s}function ae(a,s,e=a,t){if(s===te)return s;let i=t!==void 0?e._$Co?.[t]:e._$Cl,n=fe(s)?void 0:s._$litDirective$;return i?.constructor!==n&&(i?._$AO?.(!1),n===void 0?i=void 0:(i=new n(a),i._$AT(a,e,t)),t!==void 0?(e._$Co??=[])[t]=i:e._$Cl=i),i!==void 0&&(s=ae(a,i._$AS(a,s.values),i,t)),s}var Qe,yt,Ce,xt,At,J,Tt,Mi,ee,me,fe,Ze,Oi,Be,ge,wt,$t,Z,kt,Et,Ct,Xe,o,oe,js,te,h,St,X,Di,ve,We,be,ne,Ge,Ke,Ye,Je,Fi,It,Le=w(()=>{Qe=globalThis,yt=a=>a,Ce=Qe.trustedTypes,xt=Ce?Ce.createPolicy("lit-html",{createHTML:a=>a}):void 0,At="$lit$",J=`lit$${Math.random().toFixed(9).slice(2)}$`,Tt="?"+J,Mi=`<${Tt}>`,ee=document,me=()=>ee.createComment(""),fe=a=>a===null||typeof a!="object"&&typeof a!="function",Ze=Array.isArray,Oi=a=>Ze(a)||typeof a?.[Symbol.iterator]=="function",Be=`[ +\f\r]`,ge=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,wt=/-->/g,$t=/>/g,Z=RegExp(`>|${Be}(?:([^\\s"'>=/]+)(${Be}*=${Be}*(?:[^ +\f\r"'\`<>=]|("|')|))|$)`,"g"),kt=/'/g,Et=/"/g,Ct=/^(?:script|style|textarea|title)$/i,Xe=a=>(s,...e)=>({_$litType$:a,strings:s,values:e}),o=Xe(1),oe=Xe(2),js=Xe(3),te=Symbol.for("lit-noChange"),h=Symbol.for("lit-nothing"),St=new WeakMap,X=ee.createTreeWalker(ee,129);Di=(a,s)=>{let e=a.length-1,t=[],i,n=s===2?"":s===3?"":"",c=ge;for(let d=0;d"?(c=i??ge,f=-1):v[1]===void 0?f=-2:(f=c.lastIndex-v[2].length,_=v[1],c=v[3]===void 0?Z:v[3]==='"'?Et:kt):c===Et||c===kt?c=Z:c===wt||c===$t?c=ge:(c=Z,i=void 0);let g=c===Z&&a[d+1].startsWith("/>")?" ":"";n+=c===ge?u+Mi:f>=0?(t.push(_),u.slice(0,f)+At+u.slice(f)+J+g):u+J+(f===-2?d:g)}return[Lt(a,n+(a[e]||"")+(s===2?"":s===3?"":"")),t]},ve=class a{constructor({strings:s,_$litType$:e},t){let i;this.parts=[];let n=0,c=0,d=s.length-1,u=this.parts,[_,v]=Di(s,e);if(this.el=a.createElement(_,t),X.currentNode=this.el.content,e===2||e===3){let f=this.el.content.firstChild;f.replaceWith(...f.childNodes)}for(;(i=X.nextNode())!==null&&u.length0){i.textContent=Ce?Ce.emptyScript:"";for(let g=0;g2||t[0]!==""||t[1]!==""?(this._$AH=Array(t.length-1).fill(new String),this.strings=t):this._$AH=h}_$AI(s,e=this,t,i){let n=this.strings,c=!1;if(n===void 0)s=ae(this,s,e,0),c=!fe(s)||s!==this._$AH&&s!==te,c&&(this._$AH=s);else{let d=s,u,_;for(s=n[0],u=0;u{let t=e?.renderBefore??s,i=t._$litPart$;if(i===void 0){let n=e?.renderBefore??null;t._$litPart$=i=new be(s.insertBefore(me(),n),n,void 0,e??{})}return i._$AI(a),i}});var et,k,Ui,Pt=w(()=>{_e();_e();Le();Le();et=globalThis,k=class extends V{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){let s=super.createRenderRoot();return this.renderOptions.renderBefore??=s.firstChild,s}update(s){let e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(s),this._$Do=It(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return te}};k._$litElement$=!0,k.finalized=!0,et.litElementHydrateSupport?.({LitElement:k});Ui=et.litElementPolyfillSupport;Ui?.({LitElement:k});(et.litElementVersions??=[]).push("4.2.2")});var Rt=w(()=>{});var I=w(()=>{_e();Le();Pt();Rt()});var jt=w(()=>{});function y(a){return(s,e)=>typeof e=="object"?Bi(a,s,e):((t,i,n)=>{let c=i.hasOwnProperty(n);return i.constructor.createProperty(n,t),c?Object.getOwnPropertyDescriptor(i,n):void 0})(a,s,e)}var Vi,Bi,tt=w(()=>{_e();Vi={attribute:!0,type:String,converter:ue,reflect:!1,hasChanged:Te},Bi=(a=Vi,s,e)=>{let{kind:t,metadata:i}=e,n=globalThis.litPropertyMetadata.get(i);if(n===void 0&&globalThis.litPropertyMetadata.set(i,n=new Map),t==="setter"&&((a=Object.create(a)).wrapped=!0),n.set(e.name,a),t==="accessor"){let{name:c}=e;return{set(d){let u=s.get.call(this);s.set.call(this,d),this.requestUpdate(c,u,a,!0,d)},init(d){return d!==void 0&&this.C(c,void 0,a,d),d}}}if(t==="setter"){let{name:c}=e;return function(d){let u=this[c];s.call(this,d),this.requestUpdate(c,u,a,!0,d)}}throw Error("Unsupported decorator location: "+t)}});function p(a){return y({...a,state:!0,attribute:!1})}var Ht=w(()=>{tt();});var Nt=w(()=>{});var le=w(()=>{});var qt=w(()=>{le();});var zt=w(()=>{le();});var Mt=w(()=>{le();});var Ot=w(()=>{le();});var Dt=w(()=>{le();});var M=w(()=>{jt();tt();Ht();Nt();qt();zt();Mt();Ot();Dt()});var Ut,Ft=w(()=>{Ut={maintenance:"Maintenance",objects:"Objects",tasks:"Tasks",overdue:"Overdue",due_soon:"Due Soon",triggered:"Triggered",trigger_replaced:"Trigger replaced",ok:"OK",all:"All",new_object:"+ New Object",templates_from:"From template",templates_title:"Start from a template",templates_task_count:"{n} tasks",template_created:"Created from template",onboard_hint:"Add your first object to start tracking maintenance.",edit:"Edit",duplicate:"Duplicate",task_duplicated:"Task duplicated",object_duplicated:"Object duplicated",delete:"Delete",add_task:"+ Add Task",complete:"Complete",completed:"Completed",skip:"Skip",skipped:"Skipped",missed:"Missed",reset:"Reset",snooze:"Snooze",snoozed:"Snoozed",cancel:"Cancel",bulk_select:"Select",bulk_select_all:"Select all",bulk_n_selected:"{n} selected",bulk_completed:"{n} tasks completed",bulk_archived:"{n} tasks archived",completing:"Completing\u2026",interval:"Interval",warning:"Warning",last_performed:"Last performed",next_due:"Next due",days_until_due:"Days until due",avg_duration:"Avg duration",trigger:"Trigger",trigger_type:"Trigger type",threshold_above:"Upper limit",threshold_below:"Lower limit",threshold:"Threshold",counter:"Counter",state_change:"State change",runtime:"Runtime",runtime_hours:"Target runtime (hours)",target_value:"Target value",baseline:"Baseline",target_changes:"Target changes",for_minutes:"For (minutes)",time_based:"Time-based",sensor_based:"Sensor-based",manual:"Manual",one_time:"One-time",weekdays:"Weekdays",nth_weekday:"Nth weekday of month",day_of_month:"Day of month",recurrence_on_days:"Repeat on",recurrence_occurrence:"Occurrence",recurrence_weekday:"Weekday",recurrence_day:"Day of month (1\u201331)",recurrence_last_day:"Last day of the month",recurrence_business_day:"Business days only (roll back from weekend)",recurrence_offset:"Offset (days, \xB1)",recurrence_offset_help:"Shift the date by \xB1N days, e.g. -2 = two days before.",last_day_month:"Last day of month",last_business_day_month:"Last business day",ord_1:"1st",ord_2:"2nd",ord_3:"3rd",ord_4:"4th",ord_5:"5th",ord_last:"Last",day_word:"Day",interval_value:"Interval",interval_unit:"Unit",unit_days:"Days",unit_weeks:"Weeks",unit_months:"Months",unit_years:"Years",due_date:"Due date",cleaning:"Cleaning",inspection:"Inspection",replacement:"Replacement",calibration:"Calibration",service:"Service",reading:"Reading",custom:"Custom",history:"History",cost:"Cost",report_button:"Report",report_title:"Maintenance report",report_generated:"Generated",report_times_done:"Done",report_total_cost:"Total cost",report_every:"every {n} {unit}",report_notes:"Notes",report_col_type:"Type",report_col_status:"Status",report_col_schedule:"Schedule",duration:"Duration",both:"Both",trigger_val:"Trigger value",complete_title:"Complete: ",checklist:"Checklist",require_on_completion:"Require on completion",checklist_steps_optional:"Checklist steps (optional)",checklist_placeholder:`Clean filter Replace seal -Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"{field}: too long (max {n} characters)",err_too_short:"{field}: too short (min {n} characters)",err_value_too_high:"{field}: too large (max {n})",err_value_too_low:"{field}: too small (min {n})",err_required:"{field}: required",err_wrong_type:"{field}: wrong type (expected: {type})",err_invalid_choice:"{field}: not an allowed value",err_invalid_value:"{field}: invalid value",feat_schedule_time:"Time-of-day scheduling",feat_schedule_time_desc:"Tasks become overdue at a specific time of day instead of midnight.",schedule_time_optional:"Due at time (optional, HH:MM)",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",days:"days",day:"day",today:"Today",d_overdue:"d overdue",no_tasks:"No maintenance tasks yet. Create an object to get started.",no_tasks_short:"No tasks",no_history:"No history entries yet.",show_all:"Show all",cost_duration_chart:"Cost & Duration",installed:"Installed",confirm_delete_object:"Delete this object and all its tasks?",confirm_delete_task:"Delete this task?",min:"Min",max:"Max",save:"Save",saving:"Saving\u2026",edit_task:"Edit Task",new_task:"New Maintenance Task",task_name:"Task name",maintenance_type:"Maintenance type",priority:"Priority",labels:"Labels",labels_placeholder:"e.g. safety, seasonal, tenant-visible",labels_help:"Comma-separated tags for filtering and reporting.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",schedule_type:"Schedule type",interval_days:"Interval (days)",warning_days:"Warning days",earliest_completion_days:"Earliest completion (days before due)",earliest_completion_days_help:"Leave empty to allow completing any time. 0 = only on/after the due date.",last_performed_optional:"Last performed (optional)",interval_anchor:"Interval anchor",anchor_completion:"From completion date",anchor_planned:"From planned date (no drift)",edit_object:"Edit Object",name:"Name",manufacturer_optional:"Manufacturer (optional)",model_optional:"Model (optional)",serial_number_optional:"Serial number (optional)",serial_number_label:"S/N",documentation_url_label:"Manual",object_notes_label:"Notes",sort_due_date:"Due date",sort_object:"Object name",sort_type:"Type",sort_task_name:"Task name",all_objects:"All objects",tasks_lower:"tasks",no_tasks_yet:"No tasks yet",add_first_task:"Add first task",trigger_configuration:"Trigger Configuration",entity_id:"Entity ID",comma_separated:"comma-separated",entity_logic:"Entity logic",entity_logic_any:"Any entity triggers",entity_logic_all:"All entities must trigger",entities:"entities",attribute_optional:"Attribute (optional, blank = state)",use_entity_state:"Use entity state (no attribute)",trigger_above:"Trigger above",trigger_below:"Trigger below",for_at_least_minutes:"For at least (minutes)",safety_interval_days:"Safety interval (days, optional)",safety_interval:"Safety interval (optional)",delta_mode:"Delta mode",from_state_optional:"From state (optional)",to_state_optional:"To state (optional)",documentation_url_optional:"Documentation URL (optional)",object_notes_optional:"Notes (optional)",nfc_tag_id_optional:"NFC Tag ID (optional)",nfc_tags_empty_help:"No NFC tags registered in Home Assistant yet.",nfc_tags_open_settings:"Open Tags settings",nfc_tags_refresh:"Refresh",environmental_entity_optional:"Environmental sensor (optional)",environmental_entity_helper:"e.g. sensor.outdoor_temperature \u2014 adjusts the interval based on environmental conditions",environmental_attribute_optional:"Environmental attribute (optional)",nfc_tag_id:"NFC Tag ID",nfc_linked:"NFC tag linked",nfc_link_hint:"Click to link NFC tag",responsible_user:"Responsible User",shared_with:"Shared with (rotation)",shared_with_help:"Pick multiple people to share this task; the responsible person rotates on each completion.",rotation_strategy:"Rotation",rotation_none:"No rotation",rotation_round_robin:"Round-robin",rotation_least_completed:"Least completed",rotation_random:"Random",no_user_assigned:"(No user assigned)",all_users:"All Users",my_tasks:"My Tasks",tab_calendar:"Calendar",cal_no_events:"No maintenance",cal_window_7:"7 days",cal_window_14:"14 days",cal_window_30:"30 days",cal_window_365:"1 year",cal_every_n_days:"every {n} days",cal_source_time:"Time-based",cal_source_time_adaptive:"Time-based (adaptive)",cal_source_sensor:"Sensor-based",cal_predicted:"predicted",cal_confidence_high:"high confidence",cal_confidence_medium:"medium confidence",cal_confidence_low:"low confidence",budget_monthly:"Monthly budget",budget_yearly:"Yearly budget",groups:"Groups",new_group:"New group",edit_group:"Edit group",no_groups:"No groups yet",delete_group:"Delete group",delete_group_confirm:"Delete group '{name}'?",group_select_tasks:"Select tasks",group_name_required:"Name is required",description_optional:"Description (optional)",selected:"Selected",loading_chart:"Loading chart data...",hide_outliers:"Hide outliers (sensor glitches)",was_maintenance_needed:"Was this maintenance needed?",feedback_needed:"Needed",feedback_not_needed:"Not needed",feedback_not_sure:"Not sure",suggested_interval:"Suggested interval",apply_suggestion:"Apply",reanalyze:"Re-analyze",reanalyze_result:"New analysis",reanalyze_insufficient_data:"Not enough data to produce a recommendation",data_points:"data points",dismiss_suggestion:"Dismiss",confidence_low:"Low",confidence_medium:"Medium",confidence_high:"High",recommended:"recommended",seasonal_awareness:"Seasonal Awareness",edit_seasonal_overrides:"Edit seasonal factors",seasonal_overrides_title:"Seasonal factors (override)",seasonal_overrides_hint:"Factor per month (0.1\u20135.0). Empty = learned automatically.",seasonal_override_invalid:"Invalid value",seasonal_override_range:"Factor must be between 0.1 and 5.0",clear_all:"Clear all",seasonal_chart_title:"Seasonal Factors",seasonal_learned:"Learned",seasonal_manual:"Manual",month_jan:"Jan",month_feb:"Feb",month_mar:"Mar",month_apr:"Apr",month_may:"May",month_jun:"Jun",month_jul:"Jul",month_aug:"Aug",month_sep:"Sep",month_oct:"Oct",month_nov:"Nov",month_dec:"Dec",sensor_prediction:"Sensor Prediction",degradation_trend:"Trend",trend_rising:"Rising",trend_falling:"Falling",trend_stable:"Stable",trend_insufficient_data:"Insufficient data",days_until_threshold:"Days until threshold",threshold_exceeded:"Threshold exceeded",environmental_adjustment:"Environmental factor",sensor_prediction_urgency:"Sensor predicts threshold in ~{days} days",day_short:"day",weibull_reliability_curve:"Reliability Curve",weibull_failure_probability:"Failure Probability",weibull_r_squared:"Fit R\xB2",beta_early_failures:"Early Failures",beta_random_failures:"Random Failures",beta_wear_out:"Wear-out",beta_highly_predictable:"Highly Predictable",confidence_interval:"Confidence Interval",confidence_conservative:"Conservative",confidence_aggressive:"Optimistic",current_interval_marker:"Current interval",recommended_marker:"Recommended",characteristic_life:"Characteristic life",chart_mini_sparkline:"Trend sparkline",chart_history:"Cost and duration history",chart_seasonal:"Seasonal factors, 12 months",chart_weibull:"Weibull reliability curve",chart_sparkline:"Sensor trigger value chart",days_progress:"Days progress",qr_code:"QR Code",qr_generating:"Generating QR code\u2026",qr_error:"Failed to generate QR code.",qr_error_no_url:"No HA URL configured. Please set an external or internal URL in Settings \u2192 System \u2192 Network.",save_error:"Failed to save. Please try again.",qr_print:"Print",qr_download:"Download SVG",qr_action:"Action on scan",qr_action_view:"View maintenance info",qr_action_complete:"Mark maintenance as complete",qr_url_mode:"Link type",qr_mode_companion:"Companion App",qr_mode_local:"Local (mDNS)",qr_mode_server:"Server URL",overview:"Overview",analysis:"Analysis",recent_activities:"Recent Activities",search_notes:"Search notes",avg_cost:"Avg Cost",no_advanced_features:"No advanced features enabled",no_advanced_features_hint:"Enable \u201CAdaptive Intervals\u201D or \u201CSeasonal Patterns\u201D in the integration settings to see analysis data here.",analysis_not_enough_data:"Not enough data for analysis yet.",analysis_not_enough_data_hint:"Weibull analysis requires at least 5 completed maintenances; seasonal patterns become visible after 6+ data points per month.",analysis_manual_task_hint:"Manual tasks without an interval do not generate analysis data.",completions:"completions",current:"Current",shorter:"Shorter",longer:"Longer",normal:"Normal",disabled:"Disabled",compound_logic:"Compound logic",compound:"Compound (multiple conditions)",compound_logic_and:"AND \u2014 all conditions must trigger",compound_logic_or:"OR \u2014 any condition triggers",compound_help:"Combine several sensor conditions into one trigger.",compound_no_conditions:"No conditions yet \u2014 add at least one.",compound_add_condition:"Add condition",compound_condition:"Condition",compound_remove_condition:"Remove condition",card_title:"Title",card_show_header:"Show header with statistics",card_show_actions:"Show action buttons",card_compact:"Compact mode",card_max_items:"Max items (0 = all)",card_filter_status:"Filter by status",card_filter_status_help:"Empty = show all statuses.",card_filter_objects:"Filter by objects",card_filter_objects_help:"Empty = show all objects.",card_filter_areas:"Filter by areas",card_filter_areas_help:"Empty = show all areas.",card_filter_entities:"Filter by entities (entity_ids)",card_filter_entities_help:"Pick sensor / binary_sensor entities from this integration. Empty = all.",card_loading_objects:"Loading objects\u2026",card_load_error:"Could not load objects \u2014 check the WebSocket connection.",card_no_tasks_title:"No maintenance tasks yet",card_no_tasks_cta:"\u2192 Create one in the Maintenance panel",no_objects:"No objects yet.",action_error:"Action failed. Please try again.",area_id_optional:"Area (optional)",installation_date_optional:"Installation date (optional)",warranty_expiry_optional:"Warranty expiry (optional)",warranty:"Warranty",warranty_valid_until:"valid until {date}",warranty_expires_in:"expires in {days} days",warranty_expired:"expired",cal_past_windows:"Past windows",cal_forward_windows:"Forward windows",history_edit_title:"Edit history entry",history_edit_timestamp:"Timestamp",manufacturer:"Manufacturer",model:"Model",area:"Area",actions:"Actions",view_mode_label:"View",view_cards:"Card view",view_table:"Table view",objects_table_columns_label:"Objects table columns",objects_table_columns_hint:"Choose which columns appear in the objects table view.",custom_icon_optional:"Icon (optional, e.g. mdi:wrench)",task_enabled:"Task enabled",skip_reason_prompt:"Skip this task?",reason_optional:"Reason (optional)",reset_date_prompt:"Mark task as performed?",reset_date_optional:"Last performed date (optional, defaults to today)",notes_label:"Notes",documentation_label:"Documentation",no_nfc_tag:"\u2014 No tag \u2014",dashboard:"Dashboard",tab_today:"Today",palette_placeholder:"Search objects and tasks\u2026",palette_no_results:"No matches",palette_hint:"\u2191\u2193 to navigate \xB7 Enter to open \xB7 Esc to close",today_all_caught_up:"All caught up! Nothing due this week.",today_overdue:"Overdue",today_due_today:"Due today",today_this_week:"This week",settings:"Settings",settings_features:"Advanced Features",settings_features_desc:"Enable or disable advanced features. Disabling hides them from the UI but does not delete data.",feat_adaptive:"Adaptive Scheduling",feat_adaptive_desc:"Learn optimal intervals from maintenance history",feat_predictions:"Sensor Predictions",feat_predictions_desc:"Predict trigger dates from sensor degradation",feat_seasonal:"Seasonal Adjustments",feat_seasonal_desc:"Adjust intervals based on seasonal patterns",feat_environmental:"Environmental Correlation",feat_environmental_desc:"Correlate intervals with temperature/humidity",feat_budget:"Budget Tracking",feat_budget_desc:"Track monthly and yearly maintenance spending",feat_groups:"Task Groups",feat_groups_desc:"Organize tasks into logical groups",feat_checklists:"Checklists",feat_checklists_desc:"Multi-step procedures for task completion",settings_general:"General",settings_default_warning:"Default warning days",settings_panel_enabled:"Sidebar panel",settings_panel_title:"Sidebar panel title",settings_notifications:"Notifications",settings_notify_service:"Notification service",settings_install_assist_sentences:"Install Assist sentences",settings_install_assist_sentences_hint:"Copies the voice sentences into your configuration so the classic Assist agent recognises them. A file you edited yourself is never overwritten.",test_notification:"Test notification",send_test:"Send test",testing:"Sending\u2026",test_notification_success:"Test notification sent",test_notification_failed:"Test notification failed",notify_per_person:"Per-person delivery",notify_no_own_device:"No own device \u2014 uses the household service",settings_notify_due_soon:"Notify when due soon",settings_notify_overdue:"Notify when overdue",settings_notify_triggered:"Notify when triggered",settings_interval_hours:"Repeat interval (hours, 0 = once)",settings_quiet_hours:"Quiet hours",settings_quiet_start:"Start",settings_quiet_end:"End",settings_max_per_day:"Max notifications per day (0 = unlimited)",settings_bundling:"Bundle notifications",settings_bundle_threshold:"Bundle threshold",settings_reminder_leads:"Extra reminders (days before due)",settings_reminder_leads_hint:"Comma-separated lead times, e.g. 14, 3, 0 \u2014 one extra reminder fires on each matching day. Empty = off.",settings_actions:"Mobile Action Buttons",settings_action_complete:"Show 'Complete' button",settings_action_skip:"Show 'Skip' button",settings_action_snooze:"Show 'Snooze' button",settings_weekly_digest:"Weekly digest",settings_weekly_digest_hint:"A single summary notification on Monday morning when tasks are due.",settings_warranty_reminder:"Warranty expiry reminder",settings_warranty_reminder_days:"Days before expiry",settings_warranty_reminder_hint:"Notify once when an object's warranty is this many days from expiring.",settings_snooze_hours:"Snooze duration (hours)",settings_budget:"Budget",settings_currency:"Currency",settings_budget_monthly:"Monthly budget",settings_budget_yearly:"Yearly budget",settings_budget_alerts:"Budget alerts",settings_budget_threshold:"Alert threshold (%)",settings_import_export:"Import / Export",settings_export_json:"Export JSON",settings_export_yaml:"Export YAML",settings_export_csv:"Export CSV",settings_import_csv:"Import CSV",settings_import_placeholder:"Paste JSON or CSV content here\u2026",settings_import_btn:"Import",settings_import_success:"{count} objects imported successfully.",settings_export_success:"Export downloaded.",settings_saved:"Setting saved.",settings_include_history:"Include history",settings_export_selection:"Limit to selected objects (optional)",settings_docs_archive:"Documents archive (with files)",settings_docs_archive_hint:"The JSON/YAML/CSV exports carry settings only. This ZIP includes the uploaded file contents so a restore is complete.",settings_docs_export_btn:"Download documents ZIP",settings_docs_import_btn:"Restore documents ZIP",settings_docs_import_success:"Restored: {blobs} files, {docs} documents",sort_alphabetical:"Alphabetical",sort_due_soonest:"Due soonest",sort_task_count:"Task count",sort_area:"Area",sort_assigned_user:"Assigned user",sort_group:"Group",groupby_none:"No grouping",groupby_area:"By area",groupby_group:"By group",groupby_user:"By user",filter_label:"Filter",user_label:"User",photo_label:"Photo",sort_label:"Sort",group_by_label:"Group by",state_value_help:'Use the HA state value (usually lowercase, e.g. "on"/"off"). Case is normalised on save.',target_changes_help:"Number of matching transitions before the trigger fires (default: 1).",qr_print_title:"Print QR codes",qr_print_desc:"Generate a printable page of QR codes to cut out and stick on your equipment.",qr_print_load:"Load objects",qr_print_filter:"Filter",qr_print_objects:"Objects",qr_print_actions:"Actions",qr_print_url_mode:"Link type",qr_print_estimate:"Estimated QR codes",qr_print_over_limit:"cap is 200, narrow the filter",qr_print_generate:"Generate QR codes",qr_print_generating:"Generating\u2026",qr_print_ready:"QR codes ready",qr_print_print_button:"Print",qr_print_empty:"Nothing to generate",qr_action_skip:"Skip",vacation_title:"Vacation mode",vacation_active:"active",vacation_ended:"ended",vacation_desc:"Plan a vacation: notifications are paused during the period plus a buffer of days. You can opt specific tasks back in.",vacation_enable:"Enable vacation mode",vacation_start:"Start",vacation_end:"End",vacation_buffer:"Buffer (days)",vacation_exempt_title:"Notify anyway during vacation",vacation_exempt_desc:"Pick tasks that should still notify during vacation (e.g. critical pool chemistry).",vacation_load_tasks:"Load tasks",vacation_preview_btn:"Show preview",vacation_preview_affected:"tasks affected",vacation_event_due_soon:"becomes due soon",vacation_event_overdue:"becomes overdue",vacation_event_triggered_est:"sensor trigger possible",vacation_sensor_based:"(sensor-based)",vacation_action_notify:"Notify anyway",vacation_action_unsilence:"Silence again",vacation_marked_complete:"Marked complete",vacation_marked_skip:"Skipped",vacation_end_now:"End vacation now",add:"Add",show_stats:"Show stats + graphs",hide_stats:"Hide stats",adaptive_no_data:"Not enough completion history yet for adaptive analysis. Complete this task a few more times to unlock interval recommendations and reliability charts.",suggestion_applied:"Suggested interval applied",vacation_mode:"Vacation mode",vacation_status_active:"Active now",vacation_status_scheduled:"Scheduled",vacation_status_inactive:"Inactive",vacation_end_now_confirm:"End vacation immediately?",vacation_exempt_count:"exempt",vacation_advanced:"Advanced\u2026",vacation_open_panel:"Open in panel",enable:"Enable",saved:"Saved",budget_monthly_set:"Set monthly",budget_yearly_set:"Set yearly",budget_advanced:"Currency, alerts\u2026",budget_open_panel:"Open in panel",groups_empty:"No groups yet.",group_new_placeholder:"Add group\u2026",group_delete_confirm:'Delete group "{name}"?',groups_manage_tasks:"Manage task assignments\u2026",groups_open_panel:"Open in panel",unassigned:"Unassigned",no_area:"No area",has_overdue:"Has overdue tasks",object:"Object",settings_panel_access:"Panel access",settings_panel_access_desc:"Admins always have full access. To delegate create, edit and delete to specific non-admins, switch this on and pick them below \u2014 everyone else sees only Complete and Skip.",settings_operator_write:"Allow selected users to create, edit & delete",settings_operator_write_desc:"Off: only admins can change content. On: the selected users below get full access too.",no_non_admin_users:"No non-admin users found. Add some in Settings \u2192 People.",owner_label:"Owner",feat_completion_actions:"Completion actions",feat_completion_actions_desc:"Per-task HA action on complete + quick-complete QR with pre-set values.",on_complete_action_title:"On complete: trigger HA action (optional)",on_complete_action_desc:"Calls an HA service when the task is completed \u2014 e.g. reset a counter on the device.",on_complete_action_service:"Service",on_complete_action_target:"Target entity",on_complete_action_target_hint:"Note: the entity domain must match the service \u2014 e.g. 'button.press' only works on button.*, 'counter.increment' only on counter.*, 'input_button.press' only on input_button.* etc. On a mismatch the action will silently fail (HA logs 'Referenced entities ... missing or not currently available').",on_complete_action_data:"Data (JSON, optional)",on_complete_action_test:"Validate configuration",on_complete_action_test_success:"\u2713 Configuration valid (action will fire only on task completion)",on_complete_action_test_failed:"Failed",quick_complete_defaults_title:"Quick-complete defaults (for QR scans, optional)",quick_complete_defaults_desc:"Pre-set values for quick-complete QR scans. Without these, the QR opens the complete dialog.",quick_complete_defaults_notes:"Notes",quick_complete_defaults_cost:"Cost",quick_complete_defaults_duration:"Duration (minutes)",quick_complete_defaults_feedback_none:"No feedback",quick_complete_defaults_feedback_needed:"Was needed",quick_complete_defaults_feedback_not_needed:"Not needed",quick_complete_success:"Quickly marked complete",show_all_objects:"Show all objects",show_all_tasks:"Clear filter \u2014 show all tasks",filter_to_overdue:"Filter task list to overdue only",filter_to_due_soon:"Filter task list to due-soon only",filter_to_triggered:"Filter task list to triggered only",open_task:"Open task",show_details:"Show history + stats",hide_details:"Hide details",history_empty:"No history yet.",history_edit_button:"Edit entry",total_cost:"Total cost",times_performed:"Performed",older_entries:"older",open_in_panel:"Open in Maintenance panel",skip_reason:"Skip reason (optional)",reset_to_date:"Reset last_performed to",delete_task_confirm:"Delete this task and its history?",delete_object_confirm:"Delete this object and all its tasks?",loading:"Loading\u2026",archive:"Archive",undo:"Undo",task_archived:"Task archived",object_archived:"Object archived",unarchive:"Unarchive",archived:"Archived",show_archived:"Show archived",hide_archived:"Hide archived",confirm_archive_object:"Archive this object and its tasks? They keep their history and can be unarchived later.",settings_archive:"Archive & Retention",settings_archive_desc:"Retire completed one-off tasks without deleting them. Archived items are hidden and inert but keep their history and cost.",settings_archive_oneoff_days:"Auto-archive completed one-off tasks after (days, 0 = off)",settings_delete_archived_oneoff_days:"Auto-delete archived one-off tasks after (days, 0 = never)",archive_object:"Archive object",unarchive_object:"Unarchive object",documents:"Documents",documents_empty:"No documents yet.",doc_upload:"Upload file",doc_uploading:"Uploading\u2026",doc_add_link:"Add link",doc_link_url:"URL (https://\u2026)",doc_link_title:"Title (optional)",doc_open:"Open",doc_delete_confirm:'Delete "{name}"?',doc_too_large:"File is too large (max 25 MB).",doc_upload_failed:"Upload failed.",completion_photo_optional:"Completion photo (optional)",add_photo:"Add photo",uploading:"Uploading\u2026",remove:"Remove",doc_deduped:"Already stored elsewhere \u2014 shared, no extra space used.",doc_dup_in_object:"This file is already attached to this object.",doc_link_invalid:"Only http/https links are allowed.",doc_cat_manual:"Manual",doc_cat_warranty:"Warranty",doc_cat_invoice:"Invoice",doc_cat_spare_parts:"Spare parts",doc_cat_photo:"Photo",doc_cat_other:"Other",doc_link_badge:"Link",doc_storage_title:"Document storage",doc_storage_saved:"Saved via deduplication",doc_storage_refresh:"Refresh",doc_download:"Download",doc_close:"Close",doc_camera:"Take photo",doc_drop_hint:"Drop files here",doc_task_none:"No documents linked to this task.",doc_link_existing:"Link a document\u2026",doc_attach:"Link",doc_unlink:"Unlink",doc_page:"Page",chart_range_7d:"7d",chart_range_30d:"30d",chart_range_90d:"90d",chart_range_1y:"1y",chart_since_service:"since last service",chart_no_stats:"No long-term statistics for this entity \u2014 showing maintenance-event values only",auto_complete_on_recovery:"Auto-complete when the sensor recovers",auto_complete_on_recovery_help:"Records a completion (sets last performed) when the trigger clears itself \u2014 e.g. salt refilled, filter replaced.",doc_search:"Search documents\u2026",doc_search_none:"No matching documents",link_device_optional:"Link to existing device (optional)",parent_object_optional:"Parent object (optional)",parent_none:"(No parent)",paused:"Paused",pause_object:"Pause",resume_object:"Resume",pause_until_prompt:"Freeze this object's schedules \u2014 nothing becomes due and nothing notifies until it is resumed. Optionally set an auto-resume date.",pause_until_label:"Resume on (optional)",object_paused:"Object paused",object_resumed:"Object resumed \u2014 schedules restarted",object_paused_badge:"Paused",paused_until_label:"until",replace_object:"Replace\u2026",replace_object_prompt:"Retire this object and create a successor. History and costs stay archived on the old one; tasks and documents carry over to the new one, counters start fresh.",replace_name_label:"Successor name",object_replaced:"Object replaced \u2014 successor created",reading_unit_label:"Reading unit (e.g. kWh, m\xB3)",reading_unit_help:"Shown next to the recorded value when completing this task.",reading_value_label:"Reading value",reading_label:"Reading",settings_templates_label:"Template gallery",settings_templates_hint:`Untick templates you'll never need \u2014 they disappear from the "From template" pickers (panel and config flow). Nothing else changes; you can re-enable them any time.`,worksheet:"Work sheet",worksheet_scan_view:"Scan to open the task",worksheet_scan_complete:"Scan to complete",worksheet_manual_excerpt:"Manual excerpt",worksheet_pages:"pages",worksheet_printed:"Printed",worksheet_never:"Never",card_all_caught_up:"All caught up \u2014 nothing needs attention",postpone:"Postpone",postpone_date_prompt:"Postpone this occurrence to which date?",postpone_date_label:"New due date",postponed:"Postponed",postponed_to:"Postponed to",season_window_label:"Seasonal window (months)",season_window_hint:"Only due in the selected months; off-season dates roll to the next active month. None = all year.",series_end_label:"Ends",series_end_never:"Never (repeats indefinitely)",series_end_after_count:"After a number of times",series_end_until:"On a date",series_end_count_label:"Number of times",series_end_until_label:"End date",parts_section:"Parts & consumables",part_add:"Add part",part_name:"Name",part_vendor:"Manufacturer",part_storage_location:"Storage location",part_product_url:"Product URL",part_unit:"Unit",part_cost:"Unit price",part_stock:"Stock",part_reorder_threshold:"Reorder at",part_restock_quantity:"Restock quantity",part_auto_buy:"Auto-create buy task when low",part_restock:"Adjust stock",restock_quantity_label:"Quantity bought",consumes_parts_label:"Consumes parts",shared_parts_other_objects:"Parts from other objects",shared_parts_help:"Several objects can share one stock. Completing this task takes from the owning object.",shared_part_unknown:"Unknown part",parts_load_failed:"Couldn't load this object's parts \u2014 the consumes-parts options are unavailable right now.",adopt_problem_button:"Adopt problem sensors",adopt_problem_title:"Adopt problem sensors",adopt_problem_hint:"Turn HA problem sensors (printer errors, filter warnings, low battery) into maintenance tasks that trigger while the problem is active and clear themselves when it resolves.",adopt_problem_none:"No problem sensors found that aren't already tracked.",adopt_problem_active:"active",adopt_problem_ok:"ok",adopt_problem_new_object:"(new)",adopt_problem_adopt:"Adopt selected",adopt_problem_done:"Adopted {tasks} problem sensor(s)",views_label:"Views",views_none:"\u2014 No view \u2014",views_manage:"Save / manage views",views_dialog_title:"Saved views",views_dialog_hint:"Save the current filters as a named view everyone can reuse.",views_name_placeholder:"View name",views_save_current:"Save current filters",views_none_yet:"No saved views yet.",close:"Close",trigger_hint_now:"The sensor reads {value} right now.",trigger_hint_above:"The task triggers once it rises above {target}.",trigger_hint_below:"It triggers once it falls below {target}.",trigger_hint_counter_delta:"Counts from the current reading ({value}): due at {due} (+{target}), and the count restarts after each completion.",trigger_hint_counter_delta_edit:"Counts usage since the last completion: due after +{target}; the count restarts after each completion.",trigger_hint_counter_abs:"The task becomes due once the sensor reaches {target}.",trigger_hint_runtime:"The task becomes due after {hours} h of accumulated on-time; the counter restarts after each completion.",trigger_hint_state_change:"The task becomes due after {count} state change(s).",trigger_hint_state_change_to:"The task becomes due after {count} change(s) to \u201C{state}\u201D.",trigger_hint_state_now:"Current state: {value}.",adopt_problem_part:"Uses part: {name}",label_filter:"Label",all_labels:"All labels",settings_notify_scope:"Notify only for view",settings_notify_scope_all:"All tasks",settings_notify_scope_hint:"Only tasks matching the selected saved view's label/user filters send reminders. Status, sorting and grouping of the view are ignored here.",card_saved_view:"Saved view",card_saved_view_none:"None",card_saved_view_help:"Applies the view's status, user and label filters on top of the filters above. The view's sorting and grouping are panel display settings and are not applied on the card.",doc_part_none:"No documents linked to this part.",settings_templates_toggle_group:"Enable or disable all templates in this group",setups_button:"Suggested setups",setups_title:"Suggested setups (Beta)",setups_hint:"Devices of supported integrations whose consumable sensors can drive maintenance tasks. Adopting creates the object and wires each task to its sensor \u2014 it triggers when the consumable runs low and resolves itself after replacement.",setups_none:"No supported devices with unwired consumable sensors found.",setups_adopt:"Set up selected",setups_done:"{tasks} sensor-wired tasks created.",complete_parts_used:"Parts used this time",part_delete_confirm:"Delete part '{name}'? Its stock tracking, task links and any open buy reminder will be removed.",baseline_start_value:"Start reading (optional)",baseline_start_help:"Counting starts from this reading. Leave empty to count from the current value; enter the reading at the last service so usage since then already counts.",setups_baseline_hint:"reading at last service (optional)",baseline_start_help_edit:"Leave empty to keep the existing counting. Entering a value re-anchors the counting (e.g. the reading at the last service).",baseline_current_effective:"Currently effective start value: {value}",runtime_on_states:"Active states (comma-separated)",runtime_on_states_help:"States that count as running \u2014 default: on. E.g. mowing, cleaning, printing. With an attribute selected, its values are matched instead.",setups_target_new:"Create new: {name}",schedule_preview_title:"Next dates",schedule_preview_ontime:"Assuming on-time completion.",schedule_preview_ends:"(series ends)",adopt_problem_responsible:"Responsible user for all adopted tasks (optional)",adopt_problem_configure:"Configure",history_auto:"Automatic",battery_fleet_title:"Battery fleet",battery_fleet_none_low:"All batteries OK \u2014 nothing to replace.",battery_fleet_buy_now:"Buy now",battery_fleet_soon:"Needed soon",battery_fleet_soon_hint:"Predicted from the last replacement date \u2014 order ahead.",battery_fleet_mark_all:"Mark all replaced",battery_fleet_mark_one:"Mark this battery replaced",battery_fleet_offline:"offline",battery_fleet_trigger_lost:"This task's sensor trigger was lost \u2014 it will not fire or auto-complete.",battery_fleet_repair:"Repair",battery_fleet_exclude:"Exclude from the fleet",battery_fleet_excluded:"Excluded",battery_fleet_include:"Track again",battery_fleet_total:"{n} batteries tracked",battery_fleet_setup_button:"Battery fleet",battery_fleet_setup_done:"Battery fleet set up \u2014 one task tracks all your batteries.",update_banner:"A newer version of Maintenance Supporter is on the server \u2014 reload to update the panel.",update_reload:"Reload"}});var ie,Vt=$(()=>{"use strict";ie={ok:"var(--success-color, #4caf50)",due_soon:"var(--warning-color, #ff9800)",overdue:"var(--error-color, #f44336)",triggered:"var(--deep-orange-color, #ff5722)",archived:"var(--disabled-color, #9e9e9e)",paused:"var(--info-color, #2196f3)"}});function je(a){let s=(a||it).toLowerCase();return s.startsWith("pt")&&s.endsWith("br")?"pt-br":s.substring(0,2)}function r(a,s){let e=je(s);return se[e]?.[a]??se.en[a]??a}function He(a){let s=je(a);return s===it||s in se}function Ne(a){let s=je(a);return s===it||s in se||!Gi.has(s)?Promise.resolve():(s in ye||(ye[s]=fetch(`${Ki}/${s}.json`).then(e=>e.ok?e.json():null).then(e=>{e?se[s]=e:delete ye[s]}).catch(()=>{delete ye[s]})),ye[s])}function xe(a){let s=je(a);return{de:"de-DE",en:"en-US",nl:"nl-NL",fr:"fr-FR",it:"it-IT",es:"es-ES",pt:"pt-PT",ru:"ru-RU",uk:"uk-UA",zh:"zh-CN",da:"da-DK",fi:"fi-FI",nb:"nb-NO",ja:"ja-JP",hi:"hi-IN",pl:"pl-PL",cs:"cs-CZ",sv:"sv-SE","pt-br":"pt-BR",hu:"hu-HU",ko:"ko-KR",tr:"tr-TR"}[s]??"en-US"}function Wt(a){a&&(Pe.date=a.date_format,Pe.time=a.time_format)}function Gt(a,s){let e=String(a.getDate()).padStart(2,"0"),t=String(a.getMonth()+1).padStart(2,"0"),i=String(a.getFullYear());switch(Pe.date){case"DMY":return`${e}/${t}/${i}`;case"MDY":return`${t}/${e}/${i}`;case"YMD":return`${i}-${t}-${e}`;case"system":return a.toLocaleDateString(void 0,{day:"2-digit",month:"2-digit",year:"numeric"});default:return a.toLocaleDateString(xe(s),{day:"2-digit",month:"2-digit",year:"numeric"})}}function Ji(a,s){switch(Pe.time){case"12":return a.toLocaleTimeString(xe(s),{hour:"2-digit",minute:"2-digit",hour12:!0});case"24":return a.toLocaleTimeString(xe(s),{hour:"2-digit",minute:"2-digit",hour12:!1});case"system":return a.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"});default:return a.toLocaleTimeString(xe(s),{hour:"2-digit",minute:"2-digit"})}}function B(a,s){if(!a)return"\u2014";try{let e=a.includes("T")?a:a+"T00:00:00";return Gt(new Date(e),s)}catch{return a}}function Kt(a,s){if(!a)return"\u2014";try{let e=new Date(a);return Gt(e,s)+" "+Ji(e,s)}catch{return a}}function st(a,s){if(a==null)return"\u2014";let e=s||"en";return a<0?`${Math.abs(a)} ${r("d_overdue",e)}`:a===0?r("today",e):`${a} ${r(a===1?"day":"days",e)}`}function Re(a,s,e){return a==null?"\u2014":`${a} ${r("unit_"+(s||"days"),e)}`}function $e(a,s,e="long"){return new Date(Date.UTC(2024,0,1+a)).toLocaleDateString(xe(s),{weekday:e,timeZone:"UTC"})}function Yt(a,s){let e=a.schedule,t=e?.offset?` ${e.offset>0?"+":"\u2212"}${Math.abs(e.offset)}d`:"";switch(e?.kind){case"weekdays":return((e.weekdays||[]).map(i=>$e(i,s,"short")).join(" & ")||"\u2014")+t;case"nth_weekday":return e.weekday==null||e.nth==null?"\u2014":`${e.nth===-1?r("ord_last",s):r("ord_"+e.nth,s)} ${$e(e.weekday,s,"long")}${t}`;case"day_of_month":return e.day==null?"\u2014":(e.day===-1?r(e.business?"last_business_day_month":"last_day_month",s):`${r("day_word",s)} ${e.day}`)+t;case"one_time":return a.due_date?B(a.due_date,s):r("one_time",s);case"manual":return r("manual",s);case"interval":return Re(e.every,e.unit,s)}return a.schedule_type==="one_time"?a.due_date?B(a.due_date,s):r("one_time",s):a.schedule_type==="manual"?r("manual",s):a.schedule_type==="sensor_based"?r("sensor_based",s):a.interval_days!=null?Re(a.interval_days,a.interval_unit,s):"\u2014"}function Jt(a,s){a.currentTarget.dispatchEvent(new CustomEvent("hass-more-info",{detail:{entityId:s},bubbles:!0,composed:!0}))}var it,Bt,se,Gi,Ki,ye,Yi,Pe,Qt,qe,L=$(()=>{"use strict";I();Ft();Vt();it="en",Bt=(()=>{let a=window;return a.__msLocales||(a.__msLocales={store:{},inflight:{}}),a.__msLocales})(),se=Bt.store;se.en||(se.en=Ut);Gi=new Set(["de","nl","fr","it","es","pt","pt-br","ru","uk","pl","cs","sv","zh","da","fi","nb","ja","hi","hu","ko","tr"]),Ki="/maintenance_supporter_locales",ye=Bt.inflight;Yi=window,Pe=Yi.__msDateTimePrefs??={};Qt=E` +Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"{field}: too long (max {n} characters)",err_too_short:"{field}: too short (min {n} characters)",err_value_too_high:"{field}: too large (max {n})",err_value_too_low:"{field}: too small (min {n})",err_required:"{field}: required",err_wrong_type:"{field}: wrong type (expected: {type})",err_invalid_choice:"{field}: not an allowed value",err_invalid_value:"{field}: invalid value",feat_schedule_time:"Time-of-day scheduling",feat_schedule_time_desc:"Tasks become overdue at a specific time of day instead of midnight.",schedule_time_optional:"Due at time (optional, HH:MM)",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",days:"days",day:"day",today:"Today",d_overdue:"d overdue",no_tasks:"No maintenance tasks yet. Create an object to get started.",no_tasks_short:"No tasks",no_history:"No history entries yet.",show_all:"Show all",cost_duration_chart:"Cost & Duration",installed:"Installed",confirm_delete_object:"Delete this object and all its tasks?",confirm_delete_task:"Delete this task?",min:"Min",max:"Max",save:"Save",saving:"Saving\u2026",edit_task:"Edit Task",new_task:"New Maintenance Task",task_name:"Task name",maintenance_type:"Maintenance type",priority:"Priority",labels:"Labels",labels_placeholder:"e.g. safety, seasonal, tenant-visible",labels_help:"Comma-separated tags for filtering and reporting.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",schedule_type:"Schedule type",interval_days:"Interval (days)",warning_days:"Warning days",earliest_completion_days:"Earliest completion (days before due)",earliest_completion_days_help:"Leave empty to allow completing any time. 0 = only on/after the due date.",last_performed_optional:"Last performed (optional)",interval_anchor:"Interval anchor",anchor_completion:"From completion date",anchor_planned:"From planned date (no drift)",edit_object:"Edit Object",name:"Name",manufacturer_optional:"Manufacturer (optional)",model_optional:"Model (optional)",serial_number_optional:"Serial number (optional)",serial_number_label:"S/N",documentation_url_label:"Manual",object_notes_label:"Notes",sort_due_date:"Due date",sort_object:"Object name",sort_type:"Type",sort_task_name:"Task name",all_objects:"All objects",tasks_lower:"tasks",no_tasks_yet:"No tasks yet",add_first_task:"Add first task",trigger_configuration:"Trigger Configuration",entity_id:"Entity ID",comma_separated:"comma-separated",entity_logic:"Entity logic",entity_logic_any:"Any entity triggers",entity_logic_all:"All entities must trigger",entities:"entities",attribute_optional:"Attribute (optional, blank = state)",use_entity_state:"Use entity state (no attribute)",trigger_above:"Trigger above",trigger_below:"Trigger below",for_at_least_minutes:"For at least (minutes)",safety_interval_days:"Safety interval (days, optional)",safety_interval:"Safety interval (optional)",delta_mode:"Delta mode",from_state_optional:"From state (optional)",to_state_optional:"To state (optional)",documentation_url_optional:"Documentation URL (optional)",object_notes_optional:"Notes (optional)",nfc_tag_id_optional:"NFC Tag ID (optional)",nfc_tags_empty_help:"No NFC tags registered in Home Assistant yet.",nfc_tags_open_settings:"Open Tags settings",nfc_tags_refresh:"Refresh",environmental_entity_optional:"Environmental sensor (optional)",environmental_entity_helper:"e.g. sensor.outdoor_temperature \u2014 adjusts the interval based on environmental conditions",environmental_attribute_optional:"Environmental attribute (optional)",nfc_tag_id:"NFC Tag ID",nfc_linked:"NFC tag linked",nfc_link_hint:"Click to link NFC tag",responsible_user:"Responsible User",shared_with:"Shared with (rotation)",shared_with_help:"Pick multiple people to share this task; the responsible person rotates on each completion.",rotation_strategy:"Rotation",rotation_none:"No rotation",rotation_round_robin:"Round-robin",rotation_least_completed:"Least completed",rotation_random:"Random",no_user_assigned:"(No user assigned)",all_users:"All Users",my_tasks:"My Tasks",tab_calendar:"Calendar",cal_no_events:"No maintenance",cal_window_7:"7 days",cal_window_14:"14 days",cal_window_30:"30 days",cal_window_365:"1 year",cal_every_n_days:"every {n} days",cal_source_time:"Time-based",cal_source_time_adaptive:"Time-based (adaptive)",cal_source_sensor:"Sensor-based",cal_predicted:"predicted",cal_confidence_high:"high confidence",cal_confidence_medium:"medium confidence",cal_confidence_low:"low confidence",budget_monthly:"Monthly budget",budget_yearly:"Yearly budget",groups:"Groups",new_group:"New group",edit_group:"Edit group",no_groups:"No groups yet",delete_group:"Delete group",delete_group_confirm:"Delete group '{name}'?",group_select_tasks:"Select tasks",group_name_required:"Name is required",description_optional:"Description (optional)",selected:"Selected",loading_chart:"Loading chart data...",hide_outliers:"Hide outliers (sensor glitches)",was_maintenance_needed:"Was this maintenance needed?",feedback_needed:"Needed",feedback_not_needed:"Not needed",feedback_not_sure:"Not sure",suggested_interval:"Suggested interval",apply_suggestion:"Apply",reanalyze:"Re-analyze",reanalyze_result:"New analysis",reanalyze_insufficient_data:"Not enough data to produce a recommendation",data_points:"data points",dismiss_suggestion:"Dismiss",confidence_low:"Low",confidence_medium:"Medium",confidence_high:"High",recommended:"recommended",seasonal_awareness:"Seasonal Awareness",edit_seasonal_overrides:"Edit seasonal factors",seasonal_overrides_title:"Seasonal factors (override)",seasonal_overrides_hint:"Factor per month (0.1\u20135.0). Empty = learned automatically.",seasonal_override_invalid:"Invalid value",seasonal_override_range:"Factor must be between 0.1 and 5.0",clear_all:"Clear all",seasonal_chart_title:"Seasonal Factors",seasonal_learned:"Learned",seasonal_manual:"Manual",month_jan:"Jan",month_feb:"Feb",month_mar:"Mar",month_apr:"Apr",month_may:"May",month_jun:"Jun",month_jul:"Jul",month_aug:"Aug",month_sep:"Sep",month_oct:"Oct",month_nov:"Nov",month_dec:"Dec",sensor_prediction:"Sensor Prediction",degradation_trend:"Trend",trend_rising:"Rising",trend_falling:"Falling",trend_stable:"Stable",trend_insufficient_data:"Insufficient data",days_until_threshold:"Days until threshold",threshold_exceeded:"Threshold exceeded",environmental_adjustment:"Environmental factor",sensor_prediction_urgency:"Sensor predicts threshold in ~{days} days",day_short:"day",weibull_reliability_curve:"Reliability Curve",weibull_failure_probability:"Failure Probability",weibull_r_squared:"Fit R\xB2",beta_early_failures:"Early Failures",beta_random_failures:"Random Failures",beta_wear_out:"Wear-out",beta_highly_predictable:"Highly Predictable",confidence_interval:"Confidence Interval",confidence_conservative:"Conservative",confidence_aggressive:"Optimistic",current_interval_marker:"Current interval",recommended_marker:"Recommended",characteristic_life:"Characteristic life",chart_mini_sparkline:"Trend sparkline",chart_history:"Cost and duration history",chart_seasonal:"Seasonal factors, 12 months",chart_weibull:"Weibull reliability curve",chart_sparkline:"Sensor trigger value chart",days_progress:"Days progress",qr_code:"QR Code",qr_generating:"Generating QR code\u2026",qr_error:"Failed to generate QR code.",qr_error_no_url:"No HA URL configured. Please set an external or internal URL in Settings \u2192 System \u2192 Network.",save_error:"Failed to save. Please try again.",qr_print:"Print",qr_download:"Download SVG",qr_action:"Action on scan",qr_action_view:"View maintenance info",qr_action_complete:"Mark maintenance as complete",qr_url_mode:"Link type",qr_mode_companion:"Companion App",qr_mode_local:"Local (mDNS)",qr_mode_server:"Server URL",overview:"Overview",analysis:"Analysis",recent_activities:"Recent Activities",search_notes:"Search notes",avg_cost:"Avg Cost",no_advanced_features:"No advanced features enabled",no_advanced_features_hint:"Enable \u201CAdaptive Intervals\u201D or \u201CSeasonal Patterns\u201D in the integration settings to see analysis data here.",analysis_not_enough_data:"Not enough data for analysis yet.",analysis_not_enough_data_hint:"Weibull analysis requires at least 5 completed maintenances; seasonal patterns become visible after 6+ data points per month.",analysis_manual_task_hint:"Manual tasks without an interval do not generate analysis data.",completions:"completions",current:"Current",shorter:"Shorter",longer:"Longer",normal:"Normal",disabled:"Disabled",compound_logic:"Compound logic",compound:"Compound (multiple conditions)",compound_logic_and:"AND \u2014 all conditions must trigger",compound_logic_or:"OR \u2014 any condition triggers",compound_help:"Combine several sensor conditions into one trigger.",compound_no_conditions:"No conditions yet \u2014 add at least one.",compound_add_condition:"Add condition",compound_condition:"Condition",compound_remove_condition:"Remove condition",card_title:"Title",card_show_header:"Show header with statistics",card_show_actions:"Show action buttons",card_compact:"Compact mode",card_max_items:"Max items (0 = all)",card_filter_status:"Filter by status",card_filter_status_help:"Empty = show all statuses.",card_filter_objects:"Filter by objects",card_filter_objects_help:"Empty = show all objects.",card_filter_areas:"Filter by areas",card_filter_areas_help:"Empty = show all areas.",card_filter_entities:"Filter by entities (entity_ids)",card_filter_entities_help:"Pick sensor / binary_sensor entities from this integration. Empty = all.",card_loading_objects:"Loading objects\u2026",card_load_error:"Could not load objects \u2014 check the WebSocket connection.",card_no_tasks_title:"No maintenance tasks yet",card_no_tasks_cta:"\u2192 Create one in the Maintenance panel",no_objects:"No objects yet.",action_error:"Action failed. Please try again.",area_id_optional:"Area (optional)",installation_date_optional:"Installation date (optional)",warranty_expiry_optional:"Warranty expiry (optional)",warranty:"Warranty",warranty_valid_until:"valid until {date}",warranty_expires_in:"expires in {days} days",warranty_expired:"expired",cal_past_windows:"Past windows",cal_forward_windows:"Forward windows",history_edit_title:"Edit history entry",history_edit_timestamp:"Timestamp",manufacturer:"Manufacturer",model:"Model",area:"Area",actions:"Actions",view_mode_label:"View",view_cards:"Card view",view_table:"Table view",objects_table_columns_label:"Objects table columns",objects_table_columns_hint:"Choose which columns appear in the objects table view.",custom_icon_optional:"Icon (optional, e.g. mdi:wrench)",task_enabled:"Task enabled",skip_reason_prompt:"Skip this task?",reason_optional:"Reason (optional)",reset_date_prompt:"Mark task as performed?",reset_date_optional:"Last performed date (optional, defaults to today)",notes_label:"Notes",documentation_label:"Documentation",no_nfc_tag:"\u2014 No tag \u2014",dashboard:"Dashboard",tab_today:"Today",palette_placeholder:"Search objects and tasks\u2026",palette_no_results:"No matches",palette_hint:"\u2191\u2193 to navigate \xB7 Enter to open \xB7 Esc to close",today_all_caught_up:"All caught up! Nothing due this week.",today_overdue:"Overdue",today_due_today:"Due today",today_this_week:"This week",settings:"Settings",settings_features:"Advanced Features",settings_features_desc:"Enable or disable advanced features. Disabling hides them from the UI but does not delete data.",feat_adaptive:"Adaptive Scheduling",feat_adaptive_desc:"Learn optimal intervals from maintenance history",feat_predictions:"Sensor Predictions",feat_predictions_desc:"Predict trigger dates from sensor degradation",feat_seasonal:"Seasonal Adjustments",feat_seasonal_desc:"Adjust intervals based on seasonal patterns",feat_environmental:"Environmental Correlation",feat_environmental_desc:"Correlate intervals with temperature/humidity",feat_budget:"Budget Tracking",feat_budget_desc:"Track monthly and yearly maintenance spending",feat_groups:"Task Groups",feat_groups_desc:"Organize tasks into logical groups",feat_checklists:"Checklists",feat_checklists_desc:"Multi-step procedures for task completion",settings_general:"General",settings_default_warning:"Default warning days",settings_panel_enabled:"Sidebar panel",settings_panel_title:"Sidebar panel title",settings_notifications:"Notifications",settings_notify_service:"Notification service",settings_install_assist_sentences:"Install Assist sentences",settings_install_assist_sentences_hint:"Copies the voice sentences into your configuration so the classic Assist agent recognises them. A file you edited yourself is never overwritten.",test_notification:"Test notification",send_test:"Send test",testing:"Sending\u2026",test_notification_success:"Test notification sent",test_notification_failed:"Test notification failed",notify_per_person:"Per-person delivery",notify_no_own_device:"No own device \u2014 uses the household service",settings_notify_due_soon:"Notify when due soon",settings_notify_overdue:"Notify when overdue",settings_notify_triggered:"Notify when triggered",settings_interval_hours:"Repeat interval (hours, 0 = once)",settings_quiet_hours:"Quiet hours",settings_quiet_start:"Start",settings_quiet_end:"End",settings_max_per_day:"Max notifications per day (0 = unlimited)",settings_bundling:"Bundle notifications",settings_bundle_threshold:"Bundle threshold",settings_reminder_leads:"Extra reminders (days before due)",settings_reminder_leads_hint:"Comma-separated lead times, e.g. 14, 3, 0 \u2014 one extra reminder fires on each matching day. Empty = off.",settings_actions:"Mobile Action Buttons",settings_action_complete:"Show 'Complete' button",settings_action_skip:"Show 'Skip' button",settings_action_snooze:"Show 'Snooze' button",settings_weekly_digest:"Weekly digest",settings_weekly_digest_hint:"A single summary notification on Monday morning when tasks are due.",settings_warranty_reminder:"Warranty expiry reminder",settings_warranty_reminder_days:"Days before expiry",settings_warranty_reminder_hint:"Notify once when an object's warranty is this many days from expiring.",settings_snooze_hours:"Snooze duration (hours)",settings_budget:"Budget",settings_currency:"Currency",settings_budget_monthly:"Monthly budget",settings_budget_yearly:"Yearly budget",settings_budget_alerts:"Budget alerts",settings_budget_threshold:"Alert threshold (%)",settings_import_export:"Import / Export",settings_export_json:"Export JSON",settings_export_yaml:"Export YAML",settings_export_csv:"Export CSV",settings_import_csv:"Import CSV",settings_import_placeholder:"Paste JSON or CSV content here\u2026",settings_import_btn:"Import",settings_import_success:"{count} objects imported successfully.",settings_export_success:"Export downloaded.",settings_saved:"Setting saved.",settings_include_history:"Include history",settings_export_selection:"Limit to selected objects (optional)",settings_docs_archive:"Documents archive (with files)",settings_docs_archive_hint:"The JSON/YAML/CSV exports carry settings only. This ZIP includes the uploaded file contents so a restore is complete.",settings_docs_export_btn:"Download documents ZIP",settings_docs_import_btn:"Restore documents ZIP",settings_docs_import_success:"Restored: {blobs} files, {docs} documents",sort_alphabetical:"Alphabetical",sort_due_soonest:"Due soonest",sort_task_count:"Task count",sort_area:"Area",sort_assigned_user:"Assigned user",sort_group:"Group",groupby_none:"No grouping",groupby_area:"By area",groupby_group:"By group",groupby_user:"By user",filter_label:"Filter",user_label:"User",photo_label:"Photo",sort_label:"Sort",group_by_label:"Group by",state_value_help:'Use the HA state value (usually lowercase, e.g. "on"/"off"). Case is normalised on save.',target_changes_help:"Number of matching transitions before the trigger fires (default: 1).",qr_print_title:"Print QR codes",qr_print_desc:"Generate a printable page of QR codes to cut out and stick on your equipment.",qr_print_load:"Load objects",qr_print_filter:"Filter",qr_print_objects:"Objects",qr_print_actions:"Actions",qr_print_url_mode:"Link type",qr_print_estimate:"Estimated QR codes",qr_print_over_limit:"cap is 200, narrow the filter",qr_print_generate:"Generate QR codes",qr_print_generating:"Generating\u2026",qr_print_ready:"QR codes ready",qr_print_print_button:"Print",qr_print_empty:"Nothing to generate",qr_action_skip:"Skip",vacation_title:"Vacation mode",vacation_active:"active",vacation_ended:"ended",vacation_desc:"Plan a vacation: notifications are paused during the period plus a buffer of days. You can opt specific tasks back in.",vacation_enable:"Enable vacation mode",vacation_start:"Start",vacation_end:"End",vacation_buffer:"Buffer (days)",vacation_exempt_title:"Notify anyway during vacation",vacation_exempt_desc:"Pick tasks that should still notify during vacation (e.g. critical pool chemistry).",vacation_load_tasks:"Load tasks",vacation_preview_btn:"Show preview",vacation_preview_affected:"tasks affected",vacation_event_due_soon:"becomes due soon",vacation_event_overdue:"becomes overdue",vacation_event_triggered_est:"sensor trigger possible",vacation_sensor_based:"(sensor-based)",vacation_action_notify:"Notify anyway",vacation_action_unsilence:"Silence again",vacation_marked_complete:"Marked complete",vacation_marked_skip:"Skipped",vacation_end_now:"End vacation now",add:"Add",show_stats:"Show stats + graphs",hide_stats:"Hide stats",adaptive_no_data:"Not enough completion history yet for adaptive analysis. Complete this task a few more times to unlock interval recommendations and reliability charts.",suggestion_applied:"Suggested interval applied",vacation_mode:"Vacation mode",vacation_status_active:"Active now",vacation_status_scheduled:"Scheduled",vacation_status_inactive:"Inactive",vacation_end_now_confirm:"End vacation immediately?",vacation_exempt_count:"exempt",vacation_advanced:"Advanced\u2026",vacation_open_panel:"Open in panel",enable:"Enable",saved:"Saved",budget_monthly_set:"Set monthly",budget_yearly_set:"Set yearly",budget_advanced:"Currency, alerts\u2026",budget_open_panel:"Open in panel",groups_empty:"No groups yet.",group_new_placeholder:"Add group\u2026",group_delete_confirm:'Delete group "{name}"?',groups_manage_tasks:"Manage task assignments\u2026",groups_open_panel:"Open in panel",unassigned:"Unassigned",no_area:"No area",has_overdue:"Has overdue tasks",object:"Object",settings_panel_access:"Panel access",settings_panel_access_desc:"Admins always have full access. To delegate create, edit and delete to specific non-admins, switch this on and pick them below \u2014 everyone else sees only Complete and Skip.",settings_operator_write:"Allow selected users to create, edit & delete",settings_operator_write_desc:"Off: only admins can change content. On: the selected users below get full access too.",no_non_admin_users:"No non-admin users found. Add some in Settings \u2192 People.",owner_label:"Owner",feat_completion_actions:"Completion actions",feat_completion_actions_desc:"Per-task HA action on complete + quick-complete QR with pre-set values.",on_complete_action_title:"On complete: trigger HA action (optional)",on_complete_action_desc:"Calls an HA service when the task is completed \u2014 e.g. reset a counter on the device.",on_complete_action_service:"Service",on_complete_action_target:"Target entity",on_complete_action_target_hint:"Note: the entity domain must match the service \u2014 e.g. 'button.press' only works on button.*, 'counter.increment' only on counter.*, 'input_button.press' only on input_button.* etc. On a mismatch the action will silently fail (HA logs 'Referenced entities ... missing or not currently available').",on_complete_action_data:"Data (JSON, optional)",on_complete_action_test:"Validate configuration",on_complete_action_test_success:"\u2713 Configuration valid (action will fire only on task completion)",on_complete_action_test_failed:"Failed",quick_complete_defaults_title:"Quick-complete defaults (for QR scans, optional)",quick_complete_defaults_desc:"Pre-set values for quick-complete QR scans. Without these, the QR opens the complete dialog.",quick_complete_defaults_notes:"Notes",quick_complete_defaults_cost:"Cost",quick_complete_defaults_duration:"Duration (minutes)",quick_complete_defaults_feedback_none:"No feedback",quick_complete_defaults_feedback_needed:"Was needed",quick_complete_defaults_feedback_not_needed:"Not needed",quick_complete_success:"Quickly marked complete",show_all_objects:"Show all objects",show_all_tasks:"Clear filter \u2014 show all tasks",filter_to_overdue:"Filter task list to overdue only",filter_to_due_soon:"Filter task list to due-soon only",filter_to_triggered:"Filter task list to triggered only",open_task:"Open task",show_details:"Show history + stats",hide_details:"Hide details",history_empty:"No history yet.",history_edit_button:"Edit entry",total_cost:"Total cost",times_performed:"Performed",older_entries:"older",open_in_panel:"Open in Maintenance panel",skip_reason:"Skip reason (optional)",reset_to_date:"Reset last_performed to",delete_task_confirm:"Delete this task and its history?",delete_object_confirm:"Delete this object and all its tasks?",loading:"Loading\u2026",archive:"Archive",undo:"Undo",task_archived:"Task archived",object_archived:"Object archived",unarchive:"Unarchive",archived:"Archived",show_archived:"Show archived",hide_archived:"Hide archived",confirm_archive_object:"Archive this object and its tasks? They keep their history and can be unarchived later.",settings_archive:"Archive & Retention",settings_archive_desc:"Retire completed one-off tasks without deleting them. Archived items are hidden and inert but keep their history and cost.",settings_archive_oneoff_days:"Auto-archive completed one-off tasks after (days, 0 = off)",settings_delete_archived_oneoff_days:"Auto-delete archived one-off tasks after (days, 0 = never)",archive_object:"Archive object",unarchive_object:"Unarchive object",documents:"Documents",documents_empty:"No documents yet.",doc_upload:"Upload file",doc_uploading:"Uploading\u2026",doc_add_link:"Add link",doc_link_url:"URL (https://\u2026)",doc_link_title:"Title (optional)",doc_open:"Open",doc_delete_confirm:'Delete "{name}"?',doc_too_large:"File is too large (max 25 MB).",doc_upload_failed:"Upload failed.",completion_photo_optional:"Completion photo (optional)",add_photo:"Add photo",uploading:"Uploading\u2026",remove:"Remove",doc_deduped:"Already stored elsewhere \u2014 shared, no extra space used.",doc_dup_in_object:"This file is already attached to this object.",doc_link_invalid:"Only http/https links are allowed.",doc_cat_manual:"Manual",doc_cat_warranty:"Warranty",doc_cat_invoice:"Invoice",doc_cat_spare_parts:"Spare parts",doc_cat_photo:"Photo",doc_cat_other:"Other",doc_link_badge:"Link",doc_storage_title:"Document storage",doc_storage_saved:"Saved via deduplication",doc_storage_refresh:"Refresh",doc_download:"Download",doc_close:"Close",doc_camera:"Take photo",doc_drop_hint:"Drop files here",doc_task_none:"No documents linked to this task.",doc_link_existing:"Link a document\u2026",doc_attach:"Link",doc_unlink:"Unlink",doc_page:"Page",chart_range_7d:"7d",chart_range_30d:"30d",chart_range_90d:"90d",chart_range_1y:"1y",chart_since_service:"since last service",chart_no_stats:"No long-term statistics for this entity \u2014 showing maintenance-event values only",auto_complete_on_recovery:"Auto-complete when the sensor recovers",auto_complete_on_recovery_help:"Records a completion (sets last performed) when the trigger clears itself \u2014 e.g. salt refilled, filter replaced.",doc_search:"Search documents\u2026",doc_search_none:"No matching documents",link_device_optional:"Link to existing device (optional)",parent_object_optional:"Parent object (optional)",parent_none:"(No parent)",paused:"Paused",pause_object:"Pause",resume_object:"Resume",pause_until_prompt:"Freeze this object's schedules \u2014 nothing becomes due and nothing notifies until it is resumed. Optionally set an auto-resume date.",pause_until_label:"Resume on (optional)",object_paused:"Object paused",object_resumed:"Object resumed \u2014 schedules restarted",object_paused_badge:"Paused",paused_until_label:"until",replace_object:"Replace\u2026",replace_object_prompt:"Retire this object and create a successor. History and costs stay archived on the old one; tasks and documents carry over to the new one, counters start fresh.",replace_name_label:"Successor name",object_replaced:"Object replaced \u2014 successor created",reading_unit_label:"Reading unit (e.g. kWh, m\xB3)",reading_unit_help:"Shown next to the recorded value when completing this task.",reading_value_label:"Reading value",reading_label:"Reading",settings_templates_label:"Template gallery",settings_templates_hint:`Untick templates you'll never need \u2014 they disappear from the "From template" pickers (panel and config flow). Nothing else changes; you can re-enable them any time.`,worksheet:"Work sheet",worksheet_scan_view:"Scan to open the task",worksheet_scan_complete:"Scan to complete",worksheet_manual_excerpt:"Manual excerpt",worksheet_pages:"pages",worksheet_printed:"Printed",worksheet_never:"Never",card_all_caught_up:"All caught up \u2014 nothing needs attention",postpone:"Postpone",postpone_date_prompt:"Postpone this occurrence to which date?",postpone_date_label:"New due date",postponed:"Postponed",postponed_to:"Postponed to",season_window_label:"Seasonal window (months)",season_window_hint:"Only due in the selected months; off-season dates roll to the next active month. None = all year.",series_end_label:"Ends",series_end_never:"Never (repeats indefinitely)",series_end_after_count:"After a number of times",series_end_until:"On a date",series_end_count_label:"Number of times",series_end_until_label:"End date",parts_section:"Parts & consumables",part_add:"Add part",part_name:"Name",part_vendor:"Manufacturer",part_storage_location:"Storage location",part_product_url:"Product URL",part_unit:"Unit",part_cost:"Unit price",part_stock:"Stock",part_reorder_threshold:"Reorder at",part_restock_quantity:"Restock quantity",part_auto_buy:"Auto-create buy task when low",part_restock:"Adjust stock",restock_quantity_label:"Quantity bought",consumes_parts_label:"Consumes parts",shared_parts_other_objects:"Parts from other objects",shared_parts_help:"Several objects can share one stock. Completing this task takes from the owning object.",shared_part_unknown:"Unknown part",parts_load_failed:"Couldn't load this object's parts \u2014 the consumes-parts options are unavailable right now.",adopt_problem_button:"Adopt problem sensors",adopt_problem_title:"Adopt problem sensors",adopt_problem_hint:"Turn HA problem sensors (printer errors, filter warnings, low battery) into maintenance tasks that trigger while the problem is active and clear themselves when it resolves.",adopt_problem_none:"No problem sensors found that aren't already tracked.",adopt_problem_active:"active",adopt_problem_ok:"ok",adopt_problem_new_object:"(new)",adopt_problem_adopt:"Adopt selected",adopt_problem_done:"Adopted {tasks} problem sensor(s)",views_label:"Views",views_none:"\u2014 No view \u2014",views_manage:"Save / manage views",views_dialog_title:"Saved views",views_dialog_hint:"Save the current filters as a named view everyone can reuse.",views_name_placeholder:"View name",views_save_current:"Save current filters",views_none_yet:"No saved views yet.",close:"Close",trigger_hint_now:"The sensor reads {value} right now.",trigger_hint_above:"The task triggers once it rises above {target}.",trigger_hint_below:"It triggers once it falls below {target}.",trigger_hint_counter_delta:"Counts from the current reading ({value}): due at {due} (+{target}), and the count restarts after each completion.",trigger_hint_counter_delta_edit:"Counts usage since the last completion: due after +{target}; the count restarts after each completion.",trigger_hint_counter_abs:"The task becomes due once the sensor reaches {target}.",trigger_hint_runtime:"The task becomes due after {hours} h of accumulated on-time; the counter restarts after each completion.",trigger_hint_state_change:"The task becomes due after {count} state change(s).",trigger_hint_state_change_to:"The task becomes due after {count} change(s) to \u201C{state}\u201D.",trigger_hint_state_now:"Current state: {value}.",adopt_problem_part:"Uses part: {name}",label_filter:"Label",all_labels:"All labels",settings_notify_scope:"Notify only for view",settings_notify_scope_all:"All tasks",settings_notify_scope_hint:"Only tasks matching the selected saved view's label/user filters send reminders. Status, sorting and grouping of the view are ignored here.",card_saved_view:"Saved view",card_saved_view_none:"None",card_saved_view_help:"Applies the view's status, user and label filters on top of the filters above. The view's sorting and grouping are panel display settings and are not applied on the card.",doc_part_none:"No documents linked to this part.",settings_templates_toggle_group:"Enable or disable all templates in this group",setups_button:"Suggested setups",setups_title:"Suggested setups (Beta)",setups_hint:"Devices of supported integrations whose consumable sensors can drive maintenance tasks. Adopting creates the object and wires each task to its sensor \u2014 it triggers when the consumable runs low and resolves itself after replacement.",setups_none:"No supported devices with unwired consumable sensors found.",setups_adopt:"Set up selected",setups_done:"{tasks} sensor-wired tasks created.",complete_parts_used:"Parts used this time",part_delete_confirm:"Delete part '{name}'? Its stock tracking, task links and any open buy reminder will be removed.",baseline_start_value:"Start reading (optional)",baseline_start_help:"Counting starts from this reading. Leave empty to count from the current value; enter the reading at the last service so usage since then already counts.",setups_baseline_hint:"reading at last service (optional)",baseline_start_help_edit:"Leave empty to keep the existing counting. Entering a value re-anchors the counting (e.g. the reading at the last service).",baseline_current_effective:"Currently effective start value: {value}",runtime_on_states:"Active states (comma-separated)",runtime_on_states_help:"States that count as running \u2014 default: on. E.g. mowing, cleaning, printing. With an attribute selected, its values are matched instead.",setups_target_new:"Create new: {name}",schedule_preview_title:"Next dates",schedule_preview_ontime:"Assuming on-time completion.",schedule_preview_ends:"(series ends)",adopt_problem_responsible:"Responsible user for all adopted tasks (optional)",adopt_problem_configure:"Configure",history_auto:"Automatic",battery_fleet_title:"Battery fleet",battery_fleet_none_low:"All batteries OK \u2014 nothing to replace.",battery_fleet_buy_now:"Buy now",battery_fleet_soon:"Needed soon",battery_fleet_soon_hint:"Predicted from the last replacement date \u2014 order ahead.",battery_fleet_mark_all:"Mark all replaced",battery_fleet_mark_one:"Mark this battery replaced",battery_fleet_offline:"offline",battery_fleet_trigger_lost:"This task's sensor trigger was lost \u2014 it will not fire or auto-complete.",battery_fleet_repair:"Repair",battery_fleet_exclude:"Exclude from the fleet",battery_fleet_excluded:"Excluded",battery_fleet_include:"Track again",battery_fleet_all:"All tracked batteries",battery_fleet_all_hint:"Exclude a device here to drop it from the fleet before it ever reports low \u2014 a vacuum that recharges itself, or a phone that warns you on its own.",battery_fleet_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 \u2014 one task tracks all your batteries.",update_banner:"A newer version of Maintenance Supporter is on the server \u2014 reload to update the panel.",update_reload:"Reload"}});var ie,Vt=w(()=>{"use strict";ie={ok:"var(--success-color, #4caf50)",due_soon:"var(--warning-color, #ff9800)",overdue:"var(--error-color, #f44336)",triggered:"var(--deep-orange-color, #ff5722)",archived:"var(--disabled-color, #9e9e9e)",paused:"var(--info-color, #2196f3)"}});function je(a){let s=(a||it).toLowerCase();return s.startsWith("pt")&&s.endsWith("br")?"pt-br":s.substring(0,2)}function r(a,s){let e=je(s);return se[e]?.[a]??se.en[a]??a}function He(a){let s=je(a);return s===it||s in se}function Ne(a){let s=je(a);return s===it||s in se||!Gi.has(s)?Promise.resolve():(s in ye||(ye[s]=fetch(`${Ki}/${s}.json`).then(e=>e.ok?e.json():null).then(e=>{e?se[s]=e:delete ye[s]}).catch(()=>{delete ye[s]})),ye[s])}function xe(a){let s=je(a);return{de:"de-DE",en:"en-US",nl:"nl-NL",fr:"fr-FR",it:"it-IT",es:"es-ES",pt:"pt-PT",ru:"ru-RU",uk:"uk-UA",zh:"zh-CN",da:"da-DK",fi:"fi-FI",nb:"nb-NO",ja:"ja-JP",hi:"hi-IN",pl:"pl-PL",cs:"cs-CZ",sv:"sv-SE","pt-br":"pt-BR",hu:"hu-HU",ko:"ko-KR",tr:"tr-TR"}[s]??"en-US"}function Wt(a){a&&(Pe.date=a.date_format,Pe.time=a.time_format)}function Gt(a,s){let e=String(a.getDate()).padStart(2,"0"),t=String(a.getMonth()+1).padStart(2,"0"),i=String(a.getFullYear());switch(Pe.date){case"DMY":return`${e}/${t}/${i}`;case"MDY":return`${t}/${e}/${i}`;case"YMD":return`${i}-${t}-${e}`;case"system":return a.toLocaleDateString(void 0,{day:"2-digit",month:"2-digit",year:"numeric"});default:return a.toLocaleDateString(xe(s),{day:"2-digit",month:"2-digit",year:"numeric"})}}function Ji(a,s){switch(Pe.time){case"12":return a.toLocaleTimeString(xe(s),{hour:"2-digit",minute:"2-digit",hour12:!0});case"24":return a.toLocaleTimeString(xe(s),{hour:"2-digit",minute:"2-digit",hour12:!1});case"system":return a.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit"});default:return a.toLocaleTimeString(xe(s),{hour:"2-digit",minute:"2-digit"})}}function B(a,s){if(!a)return"\u2014";try{let e=a.includes("T")?a:a+"T00:00:00";return Gt(new Date(e),s)}catch{return a}}function Kt(a,s){if(!a)return"\u2014";try{let e=new Date(a);return Gt(e,s)+" "+Ji(e,s)}catch{return a}}function st(a,s){if(a==null)return"\u2014";let e=s||"en";return a<0?`${Math.abs(a)} ${r("d_overdue",e)}`:a===0?r("today",e):`${a} ${r(a===1?"day":"days",e)}`}function Re(a,s,e){return a==null?"\u2014":`${a} ${r("unit_"+(s||"days"),e)}`}function we(a,s,e="long"){return new Date(Date.UTC(2024,0,1+a)).toLocaleDateString(xe(s),{weekday:e,timeZone:"UTC"})}function Yt(a,s){let e=a.schedule,t=e?.offset?` ${e.offset>0?"+":"\u2212"}${Math.abs(e.offset)}d`:"";switch(e?.kind){case"weekdays":return((e.weekdays||[]).map(i=>we(i,s,"short")).join(" & ")||"\u2014")+t;case"nth_weekday":return e.weekday==null||e.nth==null?"\u2014":`${e.nth===-1?r("ord_last",s):r("ord_"+e.nth,s)} ${we(e.weekday,s,"long")}${t}`;case"day_of_month":return e.day==null?"\u2014":(e.day===-1?r(e.business?"last_business_day_month":"last_day_month",s):`${r("day_word",s)} ${e.day}`)+t;case"one_time":return a.due_date?B(a.due_date,s):r("one_time",s);case"manual":return r("manual",s);case"interval":return Re(e.every,e.unit,s)}return a.schedule_type==="one_time"?a.due_date?B(a.due_date,s):r("one_time",s):a.schedule_type==="manual"?r("manual",s):a.schedule_type==="sensor_based"?r("sensor_based",s):a.interval_days!=null?Re(a.interval_days,a.interval_unit,s):"\u2014"}function Jt(a,s){a.currentTarget.dispatchEvent(new CustomEvent("hass-more-info",{detail:{entityId:s},bubbles:!0,composed:!0}))}var it,Bt,se,Gi,Ki,ye,Yi,Pe,Qt,qe,L=w(()=>{"use strict";I();Ft();Vt();it="en",Bt=(()=>{let a=window;return a.__msLocales||(a.__msLocales={store:{},inflight:{}}),a.__msLocales})(),se=Bt.store;se.en||(se.en=Ut);Gi=new Set(["de","nl","fr","it","es","pt","pt-br","ru","uk","pl","cs","sv","zh","da","fi","nb","ja","hi","hu","ko","tr"]),Ki="/maintenance_supporter_locales",ye=Bt.inflight;Yi=window,Pe=Yi.__msDateTimePrefs??={};Qt=E` .field { display: flex; flex-direction: column; gap: 4px; } .field-label { font-size: 12px; color: var(--secondary-text-color); } .field-input { @@ -1179,7 +1179,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" .stat-item .stat-label { font-size: 11px; white-space: normal; text-align: center; line-height: 1.2; } .stat-value { font-size: 20px; } } -`});var ce,rt=$(()=>{"use strict";ce=class{constructor(s){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=s}updateHass(s){this.hass=s}async getUsers(s=!1){let e=Date.now();if(!s&&this.usersCache&&e-this.cacheTimestampt.id===s)?.name||null}getUser(s){return!s||!this.usersCache?null:this.usersCache.find(e=>e.id===s)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(s){return s?s===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}}});function Q(a){return`${a.entry_id??""}\0${a.part_id}`}function Qi(a,s,e,t){let i=!!a.entry_id&&a.entry_id!==s,n=i?a.entry_id:s,c=e.find(v=>v.entry_id===n),d=(c?.parts||[]).find(v=>v.id===a.part_id)||null,u=i&&c?.object?.name||"",_=d?.name||r("shared_part_unknown",t);return{part:d,foreign:i,ownerName:u,label:u?`${_} (${u})`:_}}function Zt(a,s,e,t){let n=(e.find(d=>d.entry_id===s)?.parts||[]).map(d=>({...d})),c=new Set(n.map(d=>Q({part_id:d.id})));for(let d of a?.consumes_parts||[]){if(!d.entry_id||d.entry_id===s)continue;let u=Q(d);if(c.has(u))continue;c.add(u);let{part:_,ownerName:v}=Qi(d,s,e,t);n.push({id:d.part_id,name:_?.name||r("shared_part_unknown",t),unit:_?.unit,stock:_?.stock??null,storage_location:_?.storage_location,entry_id:d.entry_id,owner_name:v})}return n}var ze=$(()=>{"use strict";L()});function es(a,s){let e=Xi[a];if(!e)return a;let t=r(e,s);return t&&t!==e?t:a}function ts(a){let e=a.match(/data\['([^']+)'\]/)?.[1],t;return(t=a.match(/length of value must be at most (\d+)/))?{field:e,rule:"too_long",param:t[1]}:(t=a.match(/length of value must be at least (\d+)/))?{field:e,rule:"too_short",param:t[1]}:(t=a.match(/value must be at most (\S+)/))?{field:e,rule:"value_too_high",param:t[1]}:(t=a.match(/value must be at least (\S+)/))?{field:e,rule:"value_too_low",param:t[1]}:/required key not provided/.test(a)?{field:e,rule:"required"}:(t=a.match(/expected (\w+)/))?{field:e,rule:"wrong_type",param:t[1]}:/value must be one of/.test(a)?{field:e,rule:"invalid_choice"}:/not a valid value/.test(a)?{field:e,rule:"invalid_value"}:{field:e,rule:"unknown"}}function P(a,s,e){if(e=e??r("action_error",s),typeof a=="string")return a;if(typeof a!="object"||a===null)return e;let t=a,i=t.message||t.error?.message||"";if(!i)return e;let n=ts(i),c=n.field?es(n.field,s):"",d=u=>r(u,s).replace("{field}",c).replace("{n}",n.param??"");switch(n.rule){case"too_long":return d("err_too_long");case"too_short":return d("err_too_short");case"value_too_high":return d("err_value_too_high");case"value_too_low":return d("err_value_too_low");case"required":return d("err_required");case"wrong_type":return d("err_wrong_type").replace("{type}",n.param??"");case"invalid_choice":return d("err_invalid_choice");case"invalid_value":return d("err_invalid_value");default:return i||e}}var Xi,re=$(()=>{"use strict";L();Xi={name:"name",task_type:"maintenance_type",schedule_type:"schedule_type",interval_days:"interval_days",interval_anchor:"interval_anchor",warning_days:"warning_days",last_performed:"last_performed_optional",notes:"notes_optional",documentation_url:"documentation_url_optional",custom_icon:"custom_icon_optional",nfc_tag_id:"nfc_tag_id_optional",responsible_user_id:"responsible_user",entity_slug:"entity_slug",entity_id:"entity_id",area_id:"area_id_optional",manufacturer:"manufacturer_optional",model:"model_optional",serial_number:"serial_number_optional",installation_date:"installation_date_optional",warranty_expiry:"warranty_expiry_optional",checklist:"checklist_steps_optional",reason:"reason",feedback:"feedback",cost:"cost",duration:"duration",description:"description_optional",group_name:"name",group_description:"description_optional",environmental_entity:"environmental_entity_optional",environmental_attribute:"environmental_attribute_optional",trigger_above:"trigger_above",trigger_below:"trigger_below",trigger_for_minutes:"trigger_for_minutes"}});var Xt,Me,at=$(()=>{"use strict";Xt=["notes","cost","duration","photo","user"],Me={notes:"notes_label",cost:"cost",duration:"duration",photo:"photo_label",user:"user_label"}});var w,nt=$(()=>{"use strict";I();M();L();re();ze();at();w=class extends k{constructor(){super(...arguments);this.entryId="";this.taskId="";this.taskName="";this.lang="en";this.checklist=[];this.adaptiveEnabled=!1;this.taskType="";this.readingUnit="";this.restockDefault=null;this.parts=[];this.consumesParts=[];this.consumesInfo=[];this.requiredFields=[];this._open=!1;this._notes="";this._cost="";this._duration="";this._loading=!1;this._error="";this._checklistState={};this._feedback="needed";this._photoDocId="";this._photoPreview="";this._photoUploading=!1;this._readingValue="";this._restockQty="";this._usedParts={}}open(){this._open||(this._open=!0,this._notes="",this._cost="",this._duration="",this._error="",this._checklistState={},this._feedback="needed",this._photoDocId="",this._photoPreview="",this._photoUploading=!1,this._readingValue="",this._restockQty=this.restockDefault!==null?String(this.restockDefault):"",this._usedParts=Object.fromEntries(this.consumesParts.map(e=>[Q(e),{...e}])))}_toggleCheck(e){let t=String(e);this._checklistState={...this._checklistState,[t]:!this._checklistState[t]}}_setFeedback(e){this._feedback=e}async _onPhotoInput(e){let t=e.target,i=t.files?.[0];if(t.value="",!!i){this._photoUploading=!0,this._error="";try{let n=new FormData;n.append("entry_id",this.entryId),n.append("tags","photo"),n.append("file",i,i.name);let c=await fetch("/api/maintenance_supporter/document/upload",{method:"POST",headers:{Authorization:`Bearer ${this.hass.auth?.data?.access_token??""}`},body:n});if(!c.ok){this._error=c.status===413?r("doc_too_large",this.lang):r("doc_upload_failed",this.lang);return}let d=await c.json();d.id&&(this._photoDocId=d.id,this._photoPreview=URL.createObjectURL(i))}catch{this._error=r("doc_upload_failed",this.lang)}finally{this._photoUploading=!1}}}_removePhoto(){this._photoPreview&&URL.revokeObjectURL(this._photoPreview),this._photoDocId="",this._photoPreview=""}async _complete(){this._loading=!0,this._error="";try{let e={type:"maintenance_supporter/task/complete",entry_id:this.entryId,task_id:this.taskId};if(this._notes&&(e.notes=this._notes),this._cost){let t=parseFloat(this._cost);!isNaN(t)&&t>=0&&(e.cost=t)}if(this._duration){let t=parseInt(this._duration,10);!isNaN(t)&&t>=0&&(e.duration=t)}if(this.checklist.length>0&&(e.checklist_state=this._checklistState),this.adaptiveEnabled&&(e.feedback=this._feedback),this._photoDocId&&(e.photo_doc_id=this._photoDocId),this._readingValue!==""){let t=parseFloat(this._readingValue);isNaN(t)||(e.reading_value=t)}if(this.restockDefault!==null&&this._restockQty!==""){let t=parseFloat(this._restockQty);!isNaN(t)&&t>=1&&(e.restock_quantity=t)}this.parts.length>0&&(e.used_parts=Object.values(this._usedParts).filter(t=>Number.isFinite(t.quantity)&&t.quantity>0).map(t=>t.entry_id?{part_id:t.part_id,quantity:t.quantity,entry_id:t.entry_id}:{part_id:t.part_id,quantity:t.quantity})),await this.hass.connection.sendMessagePromise(e),this._open=!1,this.dispatchEvent(new CustomEvent("task-completed"))}catch(e){this._error=P(e,this.lang,r("save_error",this.lang))}finally{this._loading=!1}}get _missingRequired(){let e={notes:this._notes.trim()!=="",cost:this._cost.trim()!=="",duration:this._duration.trim()!=="",photo:this._photoDocId!=="",user:!!this.hass?.user};return this.requiredFields.filter(t=>!e[t])}_req(e){return this.requiredFields.includes(e)?o``:h}_close(){this._open=!1}render(){if(!this._open)return o``;let e=this.lang||this.hass?.language||"en";return o` +`});var ce,rt=w(()=>{"use strict";ce=class{constructor(s){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=s}updateHass(s){this.hass=s}async getUsers(s=!1){let e=Date.now();if(!s&&this.usersCache&&e-this.cacheTimestampt.id===s)?.name||null}getUser(s){return!s||!this.usersCache?null:this.usersCache.find(e=>e.id===s)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(s){return s?s===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}}});function Q(a){return`${a.entry_id??""}\0${a.part_id}`}function Qi(a,s,e,t){let i=!!a.entry_id&&a.entry_id!==s,n=i?a.entry_id:s,c=e.find(v=>v.entry_id===n),d=(c?.parts||[]).find(v=>v.id===a.part_id)||null,u=i&&c?.object?.name||"",_=d?.name||r("shared_part_unknown",t);return{part:d,foreign:i,ownerName:u,label:u?`${_} (${u})`:_}}function Zt(a,s,e,t){let n=(e.find(d=>d.entry_id===s)?.parts||[]).map(d=>({...d})),c=new Set(n.map(d=>Q({part_id:d.id})));for(let d of a?.consumes_parts||[]){if(!d.entry_id||d.entry_id===s)continue;let u=Q(d);if(c.has(u))continue;c.add(u);let{part:_,ownerName:v}=Qi(d,s,e,t);n.push({id:d.part_id,name:_?.name||r("shared_part_unknown",t),unit:_?.unit,stock:_?.stock??null,storage_location:_?.storage_location,entry_id:d.entry_id,owner_name:v})}return n}var ze=w(()=>{"use strict";L()});function es(a,s){let e=Xi[a];if(!e)return a;let t=r(e,s);return t&&t!==e?t:a}function ts(a){let e=a.match(/data\['([^']+)'\]/)?.[1],t;return(t=a.match(/length of value must be at most (\d+)/))?{field:e,rule:"too_long",param:t[1]}:(t=a.match(/length of value must be at least (\d+)/))?{field:e,rule:"too_short",param:t[1]}:(t=a.match(/value must be at most (\S+)/))?{field:e,rule:"value_too_high",param:t[1]}:(t=a.match(/value must be at least (\S+)/))?{field:e,rule:"value_too_low",param:t[1]}:/required key not provided/.test(a)?{field:e,rule:"required"}:(t=a.match(/expected (\w+)/))?{field:e,rule:"wrong_type",param:t[1]}:/value must be one of/.test(a)?{field:e,rule:"invalid_choice"}:/not a valid value/.test(a)?{field:e,rule:"invalid_value"}:{field:e,rule:"unknown"}}function P(a,s,e){if(e=e??r("action_error",s),typeof a=="string")return a;if(typeof a!="object"||a===null)return e;let t=a,i=t.message||t.error?.message||"";if(!i)return e;let n=ts(i),c=n.field?es(n.field,s):"",d=u=>r(u,s).replace("{field}",c).replace("{n}",n.param??"");switch(n.rule){case"too_long":return d("err_too_long");case"too_short":return d("err_too_short");case"value_too_high":return d("err_value_too_high");case"value_too_low":return d("err_value_too_low");case"required":return d("err_required");case"wrong_type":return d("err_wrong_type").replace("{type}",n.param??"");case"invalid_choice":return d("err_invalid_choice");case"invalid_value":return d("err_invalid_value");default:return i||e}}var Xi,re=w(()=>{"use strict";L();Xi={name:"name",task_type:"maintenance_type",schedule_type:"schedule_type",interval_days:"interval_days",interval_anchor:"interval_anchor",warning_days:"warning_days",last_performed:"last_performed_optional",notes:"notes_optional",documentation_url:"documentation_url_optional",custom_icon:"custom_icon_optional",nfc_tag_id:"nfc_tag_id_optional",responsible_user_id:"responsible_user",entity_slug:"entity_slug",entity_id:"entity_id",area_id:"area_id_optional",manufacturer:"manufacturer_optional",model:"model_optional",serial_number:"serial_number_optional",installation_date:"installation_date_optional",warranty_expiry:"warranty_expiry_optional",checklist:"checklist_steps_optional",reason:"reason",feedback:"feedback",cost:"cost",duration:"duration",description:"description_optional",group_name:"name",group_description:"description_optional",environmental_entity:"environmental_entity_optional",environmental_attribute:"environmental_attribute_optional",trigger_above:"trigger_above",trigger_below:"trigger_below",trigger_for_minutes:"trigger_for_minutes"}});var Xt,Me,at=w(()=>{"use strict";Xt=["notes","cost","duration","photo","user"],Me={notes:"notes_label",cost:"cost",duration:"duration",photo:"photo_label",user:"user_label"}});var $,nt=w(()=>{"use strict";I();M();L();re();ze();at();$=class extends k{constructor(){super(...arguments);this.entryId="";this.taskId="";this.taskName="";this.lang="en";this.checklist=[];this.adaptiveEnabled=!1;this.taskType="";this.readingUnit="";this.restockDefault=null;this.parts=[];this.consumesParts=[];this.consumesInfo=[];this.requiredFields=[];this._open=!1;this._notes="";this._cost="";this._duration="";this._loading=!1;this._error="";this._checklistState={};this._feedback="needed";this._photoDocId="";this._photoPreview="";this._photoUploading=!1;this._readingValue="";this._restockQty="";this._usedParts={}}open(){this._open||(this._open=!0,this._notes="",this._cost="",this._duration="",this._error="",this._checklistState={},this._feedback="needed",this._photoDocId="",this._photoPreview="",this._photoUploading=!1,this._readingValue="",this._restockQty=this.restockDefault!==null?String(this.restockDefault):"",this._usedParts=Object.fromEntries(this.consumesParts.map(e=>[Q(e),{...e}])))}_toggleCheck(e){let t=String(e);this._checklistState={...this._checklistState,[t]:!this._checklistState[t]}}_setFeedback(e){this._feedback=e}async _onPhotoInput(e){let t=e.target,i=t.files?.[0];if(t.value="",!!i){this._photoUploading=!0,this._error="";try{let n=new FormData;n.append("entry_id",this.entryId),n.append("tags","photo"),n.append("file",i,i.name);let c=await fetch("/api/maintenance_supporter/document/upload",{method:"POST",headers:{Authorization:`Bearer ${this.hass.auth?.data?.access_token??""}`},body:n});if(!c.ok){this._error=c.status===413?r("doc_too_large",this.lang):r("doc_upload_failed",this.lang);return}let d=await c.json();d.id&&(this._photoDocId=d.id,this._photoPreview=URL.createObjectURL(i))}catch{this._error=r("doc_upload_failed",this.lang)}finally{this._photoUploading=!1}}}_removePhoto(){this._photoPreview&&URL.revokeObjectURL(this._photoPreview),this._photoDocId="",this._photoPreview=""}async _complete(){this._loading=!0,this._error="";try{let e={type:"maintenance_supporter/task/complete",entry_id:this.entryId,task_id:this.taskId};if(this._notes&&(e.notes=this._notes),this._cost){let t=parseFloat(this._cost);!isNaN(t)&&t>=0&&(e.cost=t)}if(this._duration){let t=parseInt(this._duration,10);!isNaN(t)&&t>=0&&(e.duration=t)}if(this.checklist.length>0&&(e.checklist_state=this._checklistState),this.adaptiveEnabled&&(e.feedback=this._feedback),this._photoDocId&&(e.photo_doc_id=this._photoDocId),this._readingValue!==""){let t=parseFloat(this._readingValue);isNaN(t)||(e.reading_value=t)}if(this.restockDefault!==null&&this._restockQty!==""){let t=parseFloat(this._restockQty);!isNaN(t)&&t>=1&&(e.restock_quantity=t)}this.parts.length>0&&(e.used_parts=Object.values(this._usedParts).filter(t=>Number.isFinite(t.quantity)&&t.quantity>0).map(t=>t.entry_id?{part_id:t.part_id,quantity:t.quantity,entry_id:t.entry_id}:{part_id:t.part_id,quantity:t.quantity})),await this.hass.connection.sendMessagePromise(e),this._open=!1,this.dispatchEvent(new CustomEvent("task-completed"))}catch(e){this._error=P(e,this.lang,r("save_error",this.lang))}finally{this._loading=!1}}get _missingRequired(){let e={notes:this._notes.trim()!=="",cost:this._cost.trim()!=="",duration:this._duration.trim()!=="",photo:this._photoDocId!=="",user:!!this.hass?.user};return this.requiredFields.filter(t=>!e[t])}_req(e){return this.requiredFields.includes(e)?o``:h}_close(){this._open=!1}render(){if(!this._open)return o``;let e=this.lang||this.hass?.language||"en";return o`
${r("complete_title",e)}${this.taskName}
@@ -1299,7 +1299,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
- `}};w.styles=[Qt,E` + `}};$.styles=[Qt,E` .req-mark { color: var(--error-color, #f44336); margin-left: 2px; @@ -1451,7 +1451,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" color: var(--text-primary-color, #fff); border-color: var(--primary-color); } - `],l([y({attribute:!1})],w.prototype,"hass",2),l([y()],w.prototype,"entryId",2),l([y()],w.prototype,"taskId",2),l([y()],w.prototype,"taskName",2),l([y()],w.prototype,"lang",2),l([y({type:Array})],w.prototype,"checklist",2),l([y({type:Boolean})],w.prototype,"adaptiveEnabled",2),l([y()],w.prototype,"taskType",2),l([y()],w.prototype,"readingUnit",2),l([y({attribute:!1})],w.prototype,"restockDefault",2),l([y({attribute:!1})],w.prototype,"parts",2),l([y({attribute:!1})],w.prototype,"consumesParts",2),l([y({type:Array})],w.prototype,"consumesInfo",2),l([y({type:Array})],w.prototype,"requiredFields",2),l([p()],w.prototype,"_open",2),l([p()],w.prototype,"_notes",2),l([p()],w.prototype,"_cost",2),l([p()],w.prototype,"_duration",2),l([p()],w.prototype,"_loading",2),l([p()],w.prototype,"_error",2),l([p()],w.prototype,"_checklistState",2),l([p()],w.prototype,"_feedback",2),l([p()],w.prototype,"_photoDocId",2),l([p()],w.prototype,"_photoPreview",2),l([p()],w.prototype,"_photoUploading",2),l([p()],w.prototype,"_readingValue",2),l([p()],w.prototype,"_restockQty",2),l([p()],w.prototype,"_usedParts",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",w)});var R,ot=$(()=>{"use strict";I();M();R=class extends k{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1}_onInput(e){let t=e.target.value;this.value=t,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:t}}))}render(){return o` + `],l([y({attribute:!1})],$.prototype,"hass",2),l([y()],$.prototype,"entryId",2),l([y()],$.prototype,"taskId",2),l([y()],$.prototype,"taskName",2),l([y()],$.prototype,"lang",2),l([y({type:Array})],$.prototype,"checklist",2),l([y({type:Boolean})],$.prototype,"adaptiveEnabled",2),l([y()],$.prototype,"taskType",2),l([y()],$.prototype,"readingUnit",2),l([y({attribute:!1})],$.prototype,"restockDefault",2),l([y({attribute:!1})],$.prototype,"parts",2),l([y({attribute:!1})],$.prototype,"consumesParts",2),l([y({type:Array})],$.prototype,"consumesInfo",2),l([y({type:Array})],$.prototype,"requiredFields",2),l([p()],$.prototype,"_open",2),l([p()],$.prototype,"_notes",2),l([p()],$.prototype,"_cost",2),l([p()],$.prototype,"_duration",2),l([p()],$.prototype,"_loading",2),l([p()],$.prototype,"_error",2),l([p()],$.prototype,"_checklistState",2),l([p()],$.prototype,"_feedback",2),l([p()],$.prototype,"_photoDocId",2),l([p()],$.prototype,"_photoPreview",2),l([p()],$.prototype,"_photoUploading",2),l([p()],$.prototype,"_readingValue",2),l([p()],$.prototype,"_restockQty",2),l([p()],$.prototype,"_usedParts",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",$)});var R,ot=w(()=>{"use strict";I();M();R=class extends k{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1}_onInput(e){let t=e.target.value;this.value=t,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:t}}))}render(){return o`
`}_toggleWeekday(e){this._weekdays=this._weekdays.includes(e)?this._weekdays.filter(t=>t!==e):[...this._weekdays,e]}_previewScheduleDict(){if(this._scheduleType==="one_time")return this._dueDate?{kind:"one_time",due_date:this._dueDate}:null;if(lt.includes(this._scheduleType))return{...this._buildSchedule(),...this._recurrenceExtras()};let e=parseInt(this._intervalDays,10);return this._scheduleType==="manual"||!e||e<=0?null:{kind:"interval",every:e,unit:this._intervalUnit,anchor:this._intervalAnchor,...this._recurrenceExtras()}}updated(e){super.updated?.(e);for(let t of e.keys())if(m._PREVIEW_RELEVANT.has(String(t))){this._schedulePreviewRefresh();return}}_schedulePreviewRefresh(){this._previewTimer&&clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._fetchSchedulePreview()},300)}async _fetchSchedulePreview(){let e=this._open?this._previewScheduleDict():null;if(!e){this._schedulePreview=[],this._schedulePreviewEnded=!1;return}let t=++this._previewSeq;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/schedule/preview",schedule:e,...this._lastPerformed?{last_performed:this._lastPerformed}:{}});if(t!==this._previewSeq)return;this._schedulePreview=i.occurrences||[],this._schedulePreviewEnded=!!i.series_ended}catch{}}_renderSchedulePreview(){if(this._schedulePreview.length===0)return h;let e=this._lang,t=this.scheduleTimeEnabled&&this._scheduleTime?` ${this._scheduleTime}`:"",i=this._schedulePreview.map((c,d)=>{let u=new Date(`${c}T12:00:00`).getDay();return`${we(u===0?6:u-1,e,"short")} ${B(c,e)}${d===0?t:""}`}).join(" \xB7 "),n=this._scheduleType==="time_based"&&this._intervalAnchor==="completion"?o`
${r("schedule_preview_ontime",e)}
`:h;return o`
${r("schedule_preview_title",e)}: ${i}${this._schedulePreviewEnded?o` ${r("schedule_preview_ends",e)}`:h} ${n} @@ -2625,7 +2625,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:" font-size: 14px; cursor: pointer; } - `,l([y({attribute:!1})],m.prototype,"hass",2),l([y({type:Boolean,attribute:"checklists-enabled"})],m.prototype,"checklistsEnabled",2),l([y({type:Boolean,attribute:"schedule-time-enabled"})],m.prototype,"scheduleTimeEnabled",2),l([y({type:Boolean,attribute:"completion-actions-enabled"})],m.prototype,"completionActionsEnabled",2),l([y({type:Number,attribute:"default-warning-days"})],m.prototype,"defaultWarningDays",2),l([p()],m.prototype,"parts",2),l([p()],m.prototype,"_foreignOwners",2),l([p()],m.prototype,"_open",2),l([p()],m.prototype,"_loading",2),l([p()],m.prototype,"_error",2),l([p()],m.prototype,"_entryId",2),l([p()],m.prototype,"_taskId",2),l([p()],m.prototype,"_objectChoices",2),l([p()],m.prototype,"_name",2),l([p()],m.prototype,"_type",2),l([p()],m.prototype,"_scheduleType",2),l([p()],m.prototype,"_intervalDays",2),l([p()],m.prototype,"_intervalUnit",2),l([p()],m.prototype,"_dueDate",2),l([p()],m.prototype,"_warningDays",2),l([p()],m.prototype,"_earliestCompletionDays",2),l([p()],m.prototype,"_intervalAnchor",2),l([p()],m.prototype,"_weekdays",2),l([p()],m.prototype,"_nth",2),l([p()],m.prototype,"_nthWeekday",2),l([p()],m.prototype,"_domDay",2),l([p()],m.prototype,"_domLastDay",2),l([p()],m.prototype,"_domBusiness",2),l([p()],m.prototype,"_calOffset",2),l([p()],m.prototype,"_seasonMonths",2),l([p()],m.prototype,"_endsMode",2),l([p()],m.prototype,"_endsCount",2),l([p()],m.prototype,"_endsUntil",2),l([p()],m.prototype,"_schedulePreview",2),l([p()],m.prototype,"_schedulePreviewEnded",2),l([p()],m.prototype,"_notes",2),l([p()],m.prototype,"_documentationUrl",2),l([p()],m.prototype,"_customIcon",2),l([p()],m.prototype,"_priority",2),l([p()],m.prototype,"_labels",2),l([p()],m.prototype,"_enabled",2),l([p()],m.prototype,"_triggerEntityId",2),l([p()],m.prototype,"_triggerEntityIds",2),l([p()],m.prototype,"_triggerEntityLogic",2),l([p()],m.prototype,"_triggerAttribute",2),l([p()],m.prototype,"_triggerType",2),l([p()],m.prototype,"_triggerAbove",2),l([p()],m.prototype,"_triggerBelow",2),l([p()],m.prototype,"_triggerForMinutes",2),l([p()],m.prototype,"_triggerTargetValue",2),l([p()],m.prototype,"_triggerDeltaMode",2),l([p()],m.prototype,"_triggerBaselineValue",2),l([p()],m.prototype,"_liveBaselineValue",2),l([p()],m.prototype,"_autoCompleteOnRecovery",2),l([p()],m.prototype,"_triggerFromState",2),l([p()],m.prototype,"_triggerToState",2),l([p()],m.prototype,"_triggerTargetChanges",2),l([p()],m.prototype,"_triggerRuntimeHours",2),l([p()],m.prototype,"_triggerOnStates",2),l([p()],m.prototype,"_compoundLogic",2),l([p()],m.prototype,"_compoundConditions",2),l([p()],m.prototype,"_suggestedAttributes",2),l([p()],m.prototype,"_availableAttributes",2),l([p()],m.prototype,"_entityDomain",2),l([p()],m.prototype,"_lastPerformed",2),l([p()],m.prototype,"_nfcTagId",2),l([p()],m.prototype,"_readingUnit",2),l([p()],m.prototype,"_consumesParts",2),l([p()],m.prototype,"_partsLoadFailed",2),l([p()],m.prototype,"_availableTags",2),l([p()],m.prototype,"_responsibleUserId",2),l([p()],m.prototype,"_assigneePool",2),l([p()],m.prototype,"_rotationStrategy",2),l([p()],m.prototype,"_availableUsers",2),l([p()],m.prototype,"_checklistText",2),l([p()],m.prototype,"_requiredCompletion",2),l([p()],m.prototype,"_scheduleTime",2),l([p()],m.prototype,"_actionService",2),l([p()],m.prototype,"_actionTargetEntity",2),l([p()],m.prototype,"_actionData",2),l([p()],m.prototype,"_actionDataJsonFallback",2),l([p()],m.prototype,"_actionTesting",2),l([p()],m.prototype,"_actionTestResult",2),l([p()],m.prototype,"_actionTestError",2),l([p()],m.prototype,"_qcNotes",2),l([p()],m.prototype,"_qcCost",2),l([p()],m.prototype,"_qcDuration",2),l([p()],m.prototype,"_qcFeedback",2),l([p()],m.prototype,"_environmentalEntity",2),l([p()],m.prototype,"_environmentalAttribute",2);ct=m;customElements.get("maintenance-task-dialog")||customElements.define("maintenance-task-dialog",ct)});var W,si=$(()=>{"use strict";I();M();L();re();W=class extends k{constructor(){super(...arguments);this._open=!1;this._saving=!1;this._error="";this._draft=null;this._originalSnapshot=null}get _lang(){return this.hass?.language||"en"}openEdit(e){this._draft={...e},this._originalSnapshot={...e},this._error="",this._open=!0}close(){this._open=!1,this._error="",this._draft=null,this._originalSnapshot=null}_set(e,t){this._draft&&(this._draft={...this._draft,[e]:t})}async _save(){if(!(!this._draft||!this._originalSnapshot)){this._saving=!0,this._error="";try{let e={type:"maintenance_supporter/task/history/update",entry_id:this._draft.entry_id,task_id:this._draft.task_id,original_timestamp:this._originalSnapshot.original_timestamp};if(this._draft.timestamp!==this._originalSnapshot.timestamp&&(e.timestamp=this._draft.timestamp),this._draft.notes!==this._originalSnapshot.notes&&(e.notes=this._draft.notes),this._draft.cost!==this._originalSnapshot.cost&&(e.cost=this._draft.cost),this._draft.duration!==this._originalSnapshot.duration&&(e.duration=this._draft.duration),this._draft.completed_by!==this._originalSnapshot.completed_by&&(e.completed_by=this._draft.completed_by),Object.keys(e).filter(i=>!["type","entry_id","task_id","original_timestamp"].includes(i)).length===0){this.close();return}await this.hass.connection.sendMessagePromise(e),this.dispatchEvent(new CustomEvent("history-entry-saved",{detail:{entry_id:this._draft.entry_id,task_id:this._draft.task_id,new_timestamp:this._draft.timestamp},bubbles:!0,composed:!0})),this.close()}catch(e){this._error=P(e,this._lang)}finally{this._saving=!1}}}render(){if(!this._open||!this._draft)return h;let e=this._lang,t=this._draft;return o` + `,l([y({attribute:!1})],m.prototype,"hass",2),l([y({type:Boolean,attribute:"checklists-enabled"})],m.prototype,"checklistsEnabled",2),l([y({type:Boolean,attribute:"schedule-time-enabled"})],m.prototype,"scheduleTimeEnabled",2),l([y({type:Boolean,attribute:"completion-actions-enabled"})],m.prototype,"completionActionsEnabled",2),l([y({type:Number,attribute:"default-warning-days"})],m.prototype,"defaultWarningDays",2),l([p()],m.prototype,"parts",2),l([p()],m.prototype,"_foreignOwners",2),l([p()],m.prototype,"_open",2),l([p()],m.prototype,"_loading",2),l([p()],m.prototype,"_error",2),l([p()],m.prototype,"_entryId",2),l([p()],m.prototype,"_taskId",2),l([p()],m.prototype,"_objectChoices",2),l([p()],m.prototype,"_name",2),l([p()],m.prototype,"_type",2),l([p()],m.prototype,"_scheduleType",2),l([p()],m.prototype,"_intervalDays",2),l([p()],m.prototype,"_intervalUnit",2),l([p()],m.prototype,"_dueDate",2),l([p()],m.prototype,"_warningDays",2),l([p()],m.prototype,"_earliestCompletionDays",2),l([p()],m.prototype,"_intervalAnchor",2),l([p()],m.prototype,"_weekdays",2),l([p()],m.prototype,"_nth",2),l([p()],m.prototype,"_nthWeekday",2),l([p()],m.prototype,"_domDay",2),l([p()],m.prototype,"_domLastDay",2),l([p()],m.prototype,"_domBusiness",2),l([p()],m.prototype,"_calOffset",2),l([p()],m.prototype,"_seasonMonths",2),l([p()],m.prototype,"_endsMode",2),l([p()],m.prototype,"_endsCount",2),l([p()],m.prototype,"_endsUntil",2),l([p()],m.prototype,"_schedulePreview",2),l([p()],m.prototype,"_schedulePreviewEnded",2),l([p()],m.prototype,"_notes",2),l([p()],m.prototype,"_documentationUrl",2),l([p()],m.prototype,"_customIcon",2),l([p()],m.prototype,"_priority",2),l([p()],m.prototype,"_labels",2),l([p()],m.prototype,"_enabled",2),l([p()],m.prototype,"_triggerEntityId",2),l([p()],m.prototype,"_triggerEntityIds",2),l([p()],m.prototype,"_triggerEntityLogic",2),l([p()],m.prototype,"_triggerAttribute",2),l([p()],m.prototype,"_triggerType",2),l([p()],m.prototype,"_triggerAbove",2),l([p()],m.prototype,"_triggerBelow",2),l([p()],m.prototype,"_triggerForMinutes",2),l([p()],m.prototype,"_triggerTargetValue",2),l([p()],m.prototype,"_triggerDeltaMode",2),l([p()],m.prototype,"_triggerBaselineValue",2),l([p()],m.prototype,"_liveBaselineValue",2),l([p()],m.prototype,"_autoCompleteOnRecovery",2),l([p()],m.prototype,"_triggerFromState",2),l([p()],m.prototype,"_triggerToState",2),l([p()],m.prototype,"_triggerTargetChanges",2),l([p()],m.prototype,"_triggerRuntimeHours",2),l([p()],m.prototype,"_triggerOnStates",2),l([p()],m.prototype,"_compoundLogic",2),l([p()],m.prototype,"_compoundConditions",2),l([p()],m.prototype,"_suggestedAttributes",2),l([p()],m.prototype,"_availableAttributes",2),l([p()],m.prototype,"_entityDomain",2),l([p()],m.prototype,"_lastPerformed",2),l([p()],m.prototype,"_nfcTagId",2),l([p()],m.prototype,"_readingUnit",2),l([p()],m.prototype,"_consumesParts",2),l([p()],m.prototype,"_partsLoadFailed",2),l([p()],m.prototype,"_availableTags",2),l([p()],m.prototype,"_responsibleUserId",2),l([p()],m.prototype,"_assigneePool",2),l([p()],m.prototype,"_rotationStrategy",2),l([p()],m.prototype,"_availableUsers",2),l([p()],m.prototype,"_checklistText",2),l([p()],m.prototype,"_requiredCompletion",2),l([p()],m.prototype,"_scheduleTime",2),l([p()],m.prototype,"_actionService",2),l([p()],m.prototype,"_actionTargetEntity",2),l([p()],m.prototype,"_actionData",2),l([p()],m.prototype,"_actionDataJsonFallback",2),l([p()],m.prototype,"_actionTesting",2),l([p()],m.prototype,"_actionTestResult",2),l([p()],m.prototype,"_actionTestError",2),l([p()],m.prototype,"_qcNotes",2),l([p()],m.prototype,"_qcCost",2),l([p()],m.prototype,"_qcDuration",2),l([p()],m.prototype,"_qcFeedback",2),l([p()],m.prototype,"_environmentalEntity",2),l([p()],m.prototype,"_environmentalAttribute",2);ct=m;customElements.get("maintenance-task-dialog")||customElements.define("maintenance-task-dialog",ct)});var W,si=w(()=>{"use strict";I();M();L();re();W=class extends k{constructor(){super(...arguments);this._open=!1;this._saving=!1;this._error="";this._draft=null;this._originalSnapshot=null}get _lang(){return this.hass?.language||"en"}openEdit(e){this._draft={...e},this._originalSnapshot={...e},this._error="",this._open=!0}close(){this._open=!1,this._error="",this._draft=null,this._originalSnapshot=null}_set(e,t){this._draft&&(this._draft={...this._draft,[e]:t})}async _save(){if(!(!this._draft||!this._originalSnapshot)){this._saving=!0,this._error="";try{let e={type:"maintenance_supporter/task/history/update",entry_id:this._draft.entry_id,task_id:this._draft.task_id,original_timestamp:this._originalSnapshot.original_timestamp};if(this._draft.timestamp!==this._originalSnapshot.timestamp&&(e.timestamp=this._draft.timestamp),this._draft.notes!==this._originalSnapshot.notes&&(e.notes=this._draft.notes),this._draft.cost!==this._originalSnapshot.cost&&(e.cost=this._draft.cost),this._draft.duration!==this._originalSnapshot.duration&&(e.duration=this._draft.duration),this._draft.completed_by!==this._originalSnapshot.completed_by&&(e.completed_by=this._draft.completed_by),Object.keys(e).filter(i=>!["type","entry_id","task_id","original_timestamp"].includes(i)).length===0){this.close();return}await this.hass.connection.sendMessagePromise(e),this.dispatchEvent(new CustomEvent("history-entry-saved",{detail:{entry_id:this._draft.entry_id,task_id:this._draft.task_id,new_timestamp:this._draft.timestamp},bubbles:!0,composed:!0})),this.close()}catch(e){this._error=P(e,this._lang)}finally{this._saving=!1}}}render(){if(!this._open||!this._draft)return h;let e=this._lang,t=this._draft;return o`