Updated Scheduler and Maintainance Apps

This commit is contained in:
francop committed 2026-07-08 12:00:20 -04:00
1 parent fefc2c8b5c
commit 0f7585754d
227 files changed
+2877 -958

No files matched your search

+1 -1
View File
@@ -1 +1 @@
{"pid": 71, "version": 1, "ha_version": "2026.7.1", "start_ts": 1783446559.6159952}
{"pid": 71, "version": 1, "ha_version": "2026.7.1", "start_ts": 1783526323.4156625}
+44 -9
View File
@@ -704,11 +704,11 @@
the cycle to finish: it requires a minimum of 4 minutes to pass AND the power
to drop back to the TV baseline.'
triggers:
- trigger: numeric_state
- trigger: state
entity_id: sensor.coffee_maker_power
above: 1000
id: coffee_started
conditions:
- condition: template
value_template: '{{ states(''sensor.coffee_maker_power'') | float(0) > 1000 }}'
- condition: state
entity_id: input_boolean.coffee_maker_running
state: 'off'
@@ -907,12 +907,12 @@
target:
entity_id: camera.backyard_shed
data:
filename: /media/blink.jpg
filename: /media/blink_shed.jpg
- action: camera.snapshot
target:
entity_id: camera.backyard_shed
data:
filename: /config/www/blink.jpg
filename: /config/www/blink_shed.jpg
- delay: 00:00:02
- action: ai_task.generate_data
continue_on_error: true
@@ -922,7 +922,7 @@
instructions: Describe what you see in this image in brief. Focus on any people,
objects, animals or activities. Text needs to be max 240 characters.
attachments:
- media_content_id: media-source://media_source/local/blink.jpg
- media_content_id: media-source://media_source/local/blink_shed.jpg
media_content_type: image/jpeg
response_variable: ai_profile
- action: notify.notify
@@ -939,7 +939,7 @@
else ''Motion detected, but the AI Task integration failed to return a response.''
}}
![Snapshot](/local/blink.jpg)'
![Snapshot](/local/blink_shed.jpg)'
data:
notification_id: backyard_shed_motion
mode: queued
@@ -1125,10 +1125,45 @@
event_type: state_changed
event_data:
entity_id: event.front_door_front_door_motion
enabled: false
- trigger: state
entity_id: event.living_room_living_room_camera_motion
enabled: false
- trigger: state
entity_id: event.front_door_front_door_motion
enabled: false
- trigger: event.received
target:
entity_id: event.entryway_entryway_camera_motion
options:
event_type:
- camera_motion
- camera_person
- camera_sound
enabled: false
- trigger: event.received
target:
device_id: 290f56e5a655e3af82cbf3cf9fd22406
options:
event_type:
- camera_motion
- camera_person
- camera_sound
- ring
- trigger: event.received
target:
device_id: 679d52e0d16cf48648d11dd024b961f8
options:
event_type:
- camera_motion
- camera_sound
- camera_person
enabled: false
conditions:
- condition: template
value_template: '{{ trigger.event.data.new_state.attributes.event_type in [''motion'',
''person'', ''animal'', ''chime''] }}'
enabled: false
actions:
- action: blink.trigger_camera
target:
@@ -1160,13 +1195,13 @@
- action: notify.notify
continue_on_error: true
data:
title: Motion Detected - Backyard Shed
title: Motion Detected - Front Door
message: '{{ ai_profile.data if ai_profile is defined and ''data'' in ai_profile
else ''Motion detected, but the AI Task integration failed to return a response.''
}}'
- action: notify.persistent_notification
data:
title: Motion Detected - Backyard Shed
title: Motion Detected - Front Door
message: '{{ ai_profile.data if ai_profile is defined and ''data'' in ai_profile
else ''Motion detected, but the AI Task integration failed to return a response.''
}}
@@ -374,7 +374,7 @@ async def _async_setup_shared(hass: HomeAssistant) -> bool:
# custom working weekdays instead of the plain Mon-Fri rule (#83 follow-up).
from .helpers.workday import async_setup_business_days
async_setup_business_days(hass)
await async_setup_business_days(hass)
# Create the notification manager (shared across all entries)
hass.data[DOMAIN][NOTIFICATION_MANAGER_KEY] = NotificationManager(hass)
@@ -10,7 +10,12 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import selector
from .const import CONF_TASK_INTERVAL_UNIT
from .const import (
CONF_TASK_ENDS_COUNT,
CONF_TASK_ENDS_UNTIL,
CONF_TASK_INTERVAL_UNIT,
CONF_TASK_SEASON_MONTHS,
)
from .helpers.dates import INTERVAL_UNITS
from .helpers.entity_analyzer import EntityAnalyzer
from .helpers.schedule import (
@@ -162,6 +167,74 @@ def apply_interval_unit(target: dict[str, Any], user_input: dict[str, Any]) -> N
target[CONF_TASK_INTERVAL_UNIT] = unit
# Hardcoded English month labels for the seasonal-window multi-select — same
# "keep the config-flow i18n surface small" choice as the weekday labels above.
_MONTH_LABELS = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
def season_ends_schema(current_schedule: dict[str, Any] | None) -> dict[Any, Any]:
"""Schema fragment for the seasonal window + finite-series end, prefilled
from the task's nested ``schedule``. Only meaningful for recurring kinds —
callers gate it on schedule_type accordingly."""
cur = current_schedule or {}
season_default = [str(m) for m in (cur.get("season_months") or [])]
ends_raw = cur.get("ends")
ends: dict[str, Any] = ends_raw if isinstance(ends_raw, dict) else {}
count = ends.get("count")
until = ends.get("until")
count_key = (
vol.Optional(CONF_TASK_ENDS_COUNT, default=int(count))
if isinstance(count, int) and count >= 1
else vol.Optional(CONF_TASK_ENDS_COUNT)
)
until_key = (
vol.Optional(CONF_TASK_ENDS_UNTIL, default=str(until))
if isinstance(until, str) and until
else vol.Optional(CONF_TASK_ENDS_UNTIL)
)
return {
vol.Optional(CONF_TASK_SEASON_MONTHS, default=season_default): selector.SelectSelector(
selector.SelectSelectorConfig(
options=[selector.SelectOptionDict(value=str(i + 1), label=_MONTH_LABELS[i]) for i in range(12)],
multiple=True,
mode=selector.SelectSelectorMode.LIST,
)
),
count_key: selector.NumberSelector(
selector.NumberSelectorConfig(min=1, max=9999, step=1, mode=selector.NumberSelectorMode.BOX)
),
until_key: selector.DateSelector(),
}
def apply_season_ends(schedule: dict[str, Any], user_input: dict[str, Any]) -> None:
"""Inject the seasonal window + finite-series end from a flow step into a
(recurring) ``schedule`` dict in place — count wins over until; empties clear."""
season = sorted({int(m) for m in (user_input.get(CONF_TASK_SEASON_MONTHS) or []) if str(m).isdigit() and 1 <= int(m) <= 12})
if season:
schedule["season_months"] = season
else:
schedule.pop("season_months", None)
new_ends: dict[str, Any] = {}
count = user_input.get(CONF_TASK_ENDS_COUNT)
until = user_input.get(CONF_TASK_ENDS_UNTIL)
if isinstance(count, (int, float, str)) and str(count).strip() not in ("", "0"):
try:
n = int(float(count)) # NumberSelector may return a float
if n >= 1:
new_ends["count"] = n
except (ValueError, TypeError):
pass
elif until:
new_ends["until"] = str(until)
if new_ends:
schedule["ends"] = new_ends
else:
schedule.pop("ends", None)
async def async_get_threshold_suggestions(
hass: HomeAssistant,
trigger_entity_id: str | None,
@@ -10,10 +10,12 @@ from homeassistant.helpers import selector
from .config_flow_helpers import (
CALENDAR_KIND_VALUES,
apply_season_ends,
calendar_current,
calendar_schema,
interval_unit_selector,
schedule_from_calendar_input,
season_ends_schema,
)
from .const import (
CONF_ADVANCED_SCHEDULE_TIME,
@@ -169,6 +171,19 @@ class TaskCrudMixin:
for key in ("interval_days", "interval_unit", "interval_anchor", "due_date"):
updated_task.pop(key, None)
updated_task["schedule"] = schedule
# Seasonal window + finite-series end (recurring kinds). Calendar
# kinds carry them on their nested schedule; interval tasks get a
# minimal authoritative nested schedule only when extras are set
# (the backend carries them onto the flat-derived interval).
if edit_kind in CALENDAR_KIND_VALUES and isinstance(updated_task.get("schedule"), dict):
apply_season_ends(updated_task["schedule"], user_input)
elif edit_kind == ScheduleType.TIME_BASED:
extras: dict[str, Any] = {}
apply_season_ends(extras, user_input)
if extras:
updated_task["schedule"] = {"kind": "interval", **extras}
else:
updated_task.pop("schedule", None)
# schedule_time only present when global advanced flag is on; clear by submitting "".
if CONF_TASK_SCHEDULE_TIME in user_input:
sched = (user_input.get(CONF_TASK_SCHEDULE_TIME) or "").strip()
@@ -373,6 +388,13 @@ class TaskCrudMixin:
if sched["schedule_type"] in CALENDAR_KIND_VALUES
else dict[Any, Any]()
),
# Seasonal window + finite-series end — recurring kinds only.
**(
season_ends_schema(task.get("schedule"))
if sched["schedule_type"] == ScheduleType.TIME_BASED
or sched["schedule_type"] in CALENDAR_KIND_VALUES
else dict[Any, Any]()
),
vol.Optional(
CONF_TASK_WARNING_DAYS,
default=task.get("warning_days", get_default_warning_days(self.hass)),
@@ -270,6 +270,10 @@ CONF_TASK_LAST_PERFORMED = "last_performed"
CONF_TASK_SCHEDULE_TYPE = "schedule_type"
CONF_TASK_INTERVAL_UNIT = "interval_unit"
CONF_TASK_DUE_DATE = "due_date"
# Recurrence extras (config/options flow): seasonal window + finite-series end.
CONF_TASK_SEASON_MONTHS = "season_months"
CONF_TASK_ENDS_COUNT = "ends_count"
CONF_TASK_ENDS_UNTIL = "ends_until"
CONF_TASK_NOTES = "notes"
CONF_TASK_DOCUMENTATION_URL = "documentation_url"
CONF_TASK_ICON = "custom_icon"
@@ -870,6 +870,12 @@ class MaintenanceCoordinator(DataUpdateCoordinator[dict[str, Any]]):
state["last_planned_due"] = lpd
elif "last_planned_due" in state:
del state["last_planned_due"]
# Per-occurrence postpone (set by async_postpone_task, cleared on complete).
do = td.get("due_override")
if do is not None:
state["due_override"] = do
elif "due_override" in state:
del state["due_override"]
self._store.set_history(task_id, td.get("history", []))
if task.adaptive_config:
self._store.set_adaptive_config(task_id, task.adaptive_config)
@@ -1120,6 +1126,19 @@ class MaintenanceCoordinator(DataUpdateCoordinator[dict[str, Any]]):
),
)
async def async_postpone_task(self, task_id: str, until: date) -> None:
"""Postpone just the current occurrence to ``until`` (per-occurrence
defer). Sets a one-shot ``due_override`` that the next completion clears;
the cadence is untouched."""
merged = self._get_merged_tasks_data()
if task_id not in merged:
_LOGGER.error("Task %s not found in entry %s", task_id, self.entry.title)
return
task = MaintenanceTask.from_dict(merged[task_id])
task.due_override = until.isoformat()
await self._persist_and_signal_task_change(task_id, task)
_LOGGER.debug("Occurrence postponed to %s: %s on %s", until, task.name, self.maintenance_object.name)
async def skip_maintenance(
self,
task_id: str,
@@ -90,6 +90,9 @@ def _build_export_object(
# nth_weekday / day_of_month) that the flat fields above can't.
"schedule": Schedule.parse(tdata).to_dict(),
"last_planned_due": tdata.get("last_planned_due"),
# A pending per-occurrence postpone is user intent — round-trip it
# like last_planned_due so a backup/restore keeps the deferral.
"due_override": tdata.get("due_override"),
"warning_days": tdata.get("warning_days", DEFAULT_WARNING_DAYS),
"last_performed": tdata.get("last_performed"),
"notes": tdata.get("notes"),
@@ -100,6 +103,18 @@ def _build_export_object(
"entity_slug": tdata.get("entity_slug"),
"adaptive_config": tdata.get("adaptive_config"),
"checklist": tdata.get("checklist") or [],
"schedule_time": tdata.get("schedule_time"),
# v2.17+ / #83 task fields — persisted and user-facing, so a JSON
# backup must restore them (same field-completeness contract as #67
# for documentation_url/notes; import mirrors these keys).
"priority": tdata.get("priority", "normal"),
"labels": tdata.get("labels") or [],
"earliest_completion_days": tdata.get("earliest_completion_days"),
"on_complete_action": tdata.get("on_complete_action"),
"quick_complete_defaults": tdata.get("quick_complete_defaults"),
"assignee_pool": tdata.get("assignee_pool") or [],
"rotation_strategy": tdata.get("rotation_strategy"),
"reading_unit": tdata.get("reading_unit"),
"status": ct.get("_status", "ok"),
"days_until_due": ct.get("_days_until_due"),
"next_due": ct.get("_next_due"),
@@ -105,6 +105,7 @@ function ctx(overrides: Partial<TaskDetailContext> = {}): TaskDetailContext {
openQr: () => undefined,
duplicateTask: () => undefined,
promptReset: () => undefined,
promptPostpone: () => undefined,
snoozeTask: () => undefined,
printWorksheet: () => undefined,
deleteTask: () => undefined,
@@ -162,21 +163,32 @@ describe("task-detail renderer", () => {
expect(labels.some((l) => /archive/i.test(l))).to.be.false;
});
it("open more-menu lists edit/duplicate/reset/snooze/worksheet/delete and fires callbacks", () => {
it("open more-menu lists edit/duplicate/reset/postpone/snooze/worksheet/delete and fires callbacks", () => {
const calls: string[] = [];
const host = mount(task(), ctx({
moreMenuOpen: true,
closeMoreMenu: () => calls.push("close"),
deleteTask: () => calls.push("delete"),
promptPostpone: () => calls.push("postpone"),
snoozeTask: () => calls.push("snooze"),
printWorksheet: () => calls.push("worksheet"),
}));
const items = [...host.querySelectorAll(".popup-menu-item")];
expect(items.length).to.equal(6);
(items[3] as HTMLElement).click(); // snooze
(items[4] as HTMLElement).click(); // work sheet (v2.21)
(items[5] as HTMLElement).click(); // delete (danger)
expect(calls).to.deep.equal(["close", "snooze", "close", "worksheet", "close", "delete"]);
expect(items.length).to.equal(7);
(items[3] as HTMLElement).click(); // postpone
(items[4] as HTMLElement).click(); // snooze
(items[5] as HTMLElement).click(); // work sheet (v2.21)
(items[6] as HTMLElement).click(); // delete (danger)
expect(calls).to.deep.equal(["close", "postpone", "close", "snooze", "close", "worksheet", "close", "delete"]);
});
it("shows a postponed badge when the task has a due_override, and none otherwise", () => {
const plain = mount(task(), ctx());
expect(plain.querySelector(".postponed-badge")).to.be.null;
const postponed = mount(task({ due_override: "2026-06-20" }), ctx());
const badge = postponed.querySelector(".postponed-badge");
expect(badge, "postponed badge").to.exist;
expect(badge!.textContent).to.match(/2026|20/);
});
it("tab bar switches via setActiveTab; history tab renders the timeline", () => {
@@ -0,0 +1,184 @@
/** Tests for <maintenance-task-detail-view> — the task-detail sub-view as a
* web component (incremental step over the renderers/task-detail
* extraction). Pins the contract the component adds on top of the renderer:
* - registers and renders into LIGHT DOM (no own shadow root) so the
* panel's shadow-scoped styles keep matching
* - header / breadcrumb / actions come through the component boundary
* - callbacks in the passed TaskDetailContext still fire (Complete, tab
* switch) — props in, panel callbacks out
* - property changes re-render (new task object → new name in the DOM)
* - renders nothing until both `task` and `ctx` are set
*/
import { expect, fixture } from "@open-wc/testing";
import { html } from "lit";
import "../components/task-detail-view.js";
import type { MaintenanceTaskDetailView } from "../components/task-detail-view.js";
import type { TaskDetailContext } from "../renderers/task-detail.js";
import type { MaintenanceTask } from "../types";
import { createMockHass } from "./_test-utils.js";
function task(overrides: Record<string, unknown> = {}): MaintenanceTask {
return {
id: "t1",
name: "Filter Wechsel",
type: "cleaning",
enabled: true,
status: "due_soon",
schedule_type: "time_based",
interval_days: 30,
warning_days: 7,
days_until_due: 3,
next_due: "2026-07-10",
last_performed: "2026-06-10",
times_performed: 2,
total_cost: 50,
average_duration: 20,
history: [],
checklist: [],
is_done: false,
archived: false,
trigger_active: false,
...overrides,
} as unknown as MaintenanceTask;
}
function ctx(overrides: Partial<TaskDetailContext> = {}): TaskDetailContext {
const { hass } = createMockHass({
handler: () => ({ documents: [] }),
});
return {
lang: "en",
hass: hass as TaskDetailContext["hass"],
entryId: "entry1",
taskId: "t1",
objectName: "Pool Pump",
objectDocUrl: null,
isOperator: false,
actionLoading: false,
moreMenuOpen: false,
activeTab: "overview",
features: {
adaptive: false, predictions: false, seasonal: false,
environmental: false, budget: false, groups: false,
checklists: false, schedule_time: false, completion_actions: false,
},
currencySymbol: "€",
collapsedSections: new Set(),
costDurationToggle: "both",
suggestionDismissed: false,
sparkline: {
lang: "en",
detailStatsData: new Map(),
hasStatsService: false,
isCounterEntity: () => false,
rangeDays: 30,
setRangeDays: () => undefined,
hideOutliers: false,
setHideOutliers: () => undefined,
},
history: {
lang: "en",
hass: hass as TaskDetailContext["hass"],
filter: null,
search: "",
currencySymbol: "€",
setFilter: () => undefined,
setSearch: () => undefined,
openEdit: () => undefined,
},
getUserName: () => null,
setActiveTab: () => undefined,
toggleSection: () => undefined,
setCostDurationToggle: () => undefined,
showTaskView: () => undefined,
showObject: () => undefined,
toggleMoreMenu: () => undefined,
closeMoreMenu: () => undefined,
openEdit: () => undefined,
openComplete: () => undefined,
promptSkip: () => undefined,
toggleArchive: () => undefined,
openQr: () => undefined,
duplicateTask: () => undefined,
promptReset: () => undefined,
promptPostpone: () => undefined,
snoozeTask: () => undefined,
printWorksheet: () => undefined,
deleteTask: () => undefined,
applySuggestion: () => undefined,
reanalyze: () => undefined,
dismissSuggestion: () => undefined,
openSeasonalOverrides: () => undefined,
...overrides,
};
}
async function mount(t: MaintenanceTask, c: TaskDetailContext): Promise<MaintenanceTaskDetailView> {
const el = await fixture<MaintenanceTaskDetailView>(html`
<maintenance-task-detail-view .task=${t} .ctx=${c}></maintenance-task-detail-view>
`);
await el.updateComplete;
return el;
}
describe("maintenance-task-detail-view", () => {
it("is registered as a custom element", () => {
expect(customElements.get("maintenance-task-detail-view")).to.exist;
});
it("renders into light DOM so the panel's shadow-scoped styles keep applying", async () => {
const el = await mount(task(), ctx());
expect(el.shadowRoot, "no own shadow root").to.equal(null);
// The detail markup is queryable directly on the element (= light DOM).
expect(el.querySelector(".detail-section"), "detail section in light DOM").to.exist;
});
it("renders header, breadcrumb and status through the component boundary", async () => {
const el = await mount(task(), ctx());
expect(el.querySelector(".task-name-breadcrumb")?.textContent).to.contain("Filter Wechsel");
expect(el.querySelector(".object-name-breadcrumb")?.textContent).to.contain("Pool Pump");
expect(el.querySelector(".status-chip"), "status chip").to.exist;
expect(el.querySelector(".kpi-bar"), "KPI bar").to.exist;
});
it("routes the Complete action to the panel callback with the task", async () => {
let completed: MaintenanceTask | null = null;
const el = await mount(task(), ctx({ openComplete: (tk) => { completed = tk; } }));
const btn = [...el.querySelectorAll(".task-header-actions ha-button")]
.find((b) => b.textContent?.match(/complete/i)) as HTMLElement;
expect(btn, "complete button").to.exist;
btn.click();
expect(completed, "openComplete received the task").to.not.equal(null);
expect((completed as unknown as MaintenanceTask).id).to.equal("t1");
});
it("switches tabs via the panel-owned setActiveTab callback", async () => {
let tab = "";
const el = await mount(task(), ctx({ setActiveTab: (v) => { tab = v; } }));
const tabs = [...el.querySelectorAll(".tab-bar .tab")] as HTMLElement[];
expect(tabs.length).to.equal(2);
tabs[1].click();
expect(tab).to.equal("history");
});
it("re-renders when the task property changes", async () => {
const el = await mount(task(), ctx());
el.task = task({ name: "Pumpe entkalken" });
await el.updateComplete;
expect(el.querySelector(".task-name-breadcrumb")?.textContent).to.contain("Pumpe entkalken");
});
it("renders nothing until both task and ctx are set", async () => {
const el = await fixture<MaintenanceTaskDetailView>(html`
<maintenance-task-detail-view></maintenance-task-detail-view>
`);
await el.updateComplete;
expect(el.querySelector(".detail-section")).to.equal(null);
});
it("hides the more-menu for operators (read-only surface preserved)", async () => {
const el = await mount(task(), ctx({ isOperator: true }));
expect(el.querySelector(".more-menu-wrapper")).to.equal(null);
});
});
@@ -0,0 +1,106 @@
/**
* Lit tests for the seasonal-window + finite-series recurrence extras in
* <maintenance-task-dialog>. Pins hydration from the nested schedule and the
* outgoing `schedule` payload (backend: test_schedule.py / test_ws_io.py).
*/
import { expect, fixture, html } from "@open-wc/testing";
import "../components/task-dialog.js";
import type { MaintenanceTaskDialog } from "../components/task-dialog";
import { type SentMessage, createMockHass } from "./_test-utils.js";
async function mountDialog(): Promise<{ el: MaintenanceTaskDialog; sent: SentMessage[] }> {
const { hass, sent } = createMockHass({
handlers: {
"maintenance_supporter/task/create": () => ({ task_id: "new1" }),
"maintenance_supporter/task/update": () => ({}),
},
});
const el = await fixture<MaintenanceTaskDialog>(html`
<maintenance-task-dialog .hass=${hass}></maintenance-task-dialog>
`);
await el.updateComplete;
return { el, sent };
}
describe("task-dialog recurrence extras (season / finite series)", () => {
it("hydrates season_months and a count end from the nested schedule", async () => {
const { el } = await mountDialog();
await el.openEdit("e", {
id: "t1", name: "Mow", type: "custom", schedule_type: "time_based",
interval_days: 14, interval_unit: "days", warning_days: 7, enabled: true,
schedule: { kind: "interval", every: 14, unit: "days", season_months: [4, 5, 6], ends: { count: 6 } },
} as any);
await el.updateComplete;
expect((el as any)._seasonMonths).to.deep.equal([4, 5, 6]);
expect((el as any)._endsMode).to.equal("count");
expect((el as any)._endsCount).to.equal("6");
});
it("hydrates an until end", async () => {
const { el } = await mountDialog();
await el.openEdit("e", {
id: "t1", name: "Cure", type: "custom", schedule_type: "time_based",
interval_days: 30, warning_days: 7, enabled: true,
schedule: { kind: "interval", every: 30, unit: "days", ends: { until: "2027-01-01" } },
} as any);
await el.updateComplete;
expect((el as any)._endsMode).to.equal("until");
expect((el as any)._endsUntil).to.equal("2027-01-01");
});
it("create sends season_months + ends on an interval task's nested schedule", async () => {
const { el, sent } = await mountDialog();
await el.openCreate("e");
(el as any)._name = "Mow";
(el as any)._scheduleType = "time_based";
(el as any)._intervalDays = "14";
(el as any)._intervalUnit = "days";
(el as any)._seasonMonths = [7, 4, 5];
(el as any)._endsMode = "count";
(el as any)._endsCount = "6";
await (el as any)._save();
const msg = sent.find((m) => m.type === "maintenance_supporter/task/create") as any;
expect(msg.schedule).to.deep.equal({ kind: "interval", season_months: [4, 5, 7], ends: { count: 6 } });
expect(msg.interval_days).to.equal(14); // flat fields still carry the cadence
});
it("attaches the extras to a calendar-kind schedule too", async () => {
const { el, sent } = await mountDialog();
await el.openCreate("e");
(el as any)._name = "Gutter";
(el as any)._scheduleType = "day_of_month";
(el as any)._domDay = "15";
(el as any)._seasonMonths = [10, 11];
await (el as any)._save();
const msg = sent.find((m) => m.type === "maintenance_supporter/task/create") as any;
expect(msg.schedule).to.deep.equal({ kind: "day_of_month", day: 15, season_months: [10, 11] });
});
it("clearing season/ends on edit sends an authoritative schedule without them", async () => {
const { el, sent } = await mountDialog();
await el.openEdit("e", {
id: "t1", name: "Mow", type: "custom", schedule_type: "time_based",
interval_days: 14, warning_days: 7, enabled: true,
schedule: { kind: "interval", every: 14, unit: "days", season_months: [4, 5], ends: { count: 3 } },
} as any);
(el as any)._seasonMonths = [];
(el as any)._endsMode = "never";
await (el as any)._save();
const msg = sent.find((m) => m.type === "maintenance_supporter/task/update") as any;
expect(msg.schedule).to.deep.equal({ kind: "interval" }); // no season_months, no ends
});
it("shows the season chips + ends selector for recurring, not for one_time", async () => {
const { el } = await mountDialog();
await el.openCreate("e");
(el as any)._scheduleType = "time_based";
await el.updateComplete;
expect(el.shadowRoot!.querySelectorAll(".season-chip").length).to.equal(12);
expect(el.shadowRoot!.querySelector('select option[value="count"]')).to.exist;
(el as any)._scheduleType = "one_time";
await el.updateComplete;
expect(el.shadowRoot!.querySelectorAll(".season-chip").length).to.equal(0);
});
});
@@ -0,0 +1,37 @@
/** <maintenance-task-detail-view> — the task-detail sub-view as a web
* component. The incremental step over the renderers/task-detail
* extraction: the panel now hands the component a task + the
* TaskDetailContext instead of calling the render function inline, giving
* the sub-view a real element boundary (own update lifecycle, testable in
* isolation, future home for detail-only state).
*
* Deliberately renders into LIGHT DOM (createRenderRoot returns `this`):
* the element lives inside the panel's shadow root, so every selector in
* panel-styles.ts (.task-header, .kpi-bar, .popup-menu, …) keeps matching
* and every dialog-opening callback still runs against the panel's shadow
* root. Moving the ~1000 lines of detail CSS and the dialog ownership in
* here would be the big-bang rewrite this component exists to avoid.
*/
import { LitElement, html, nothing } from "lit";
import { property } from "lit/decorators.js";
import { renderTaskDetail, type TaskDetailContext } from "../renderers/task-detail";
import type { MaintenanceTask } from "../types";
export class MaintenanceTaskDetailView extends LitElement {
@property({ attribute: false }) public task?: MaintenanceTask;
@property({ attribute: false }) public ctx?: TaskDetailContext;
protected createRenderRoot(): HTMLElement {
return this;
}
protected render(): unknown {
if (!this.task || !this.ctx) return nothing;
return html`${renderTaskDetail(this.task, this.ctx)}`;
}
}
if (!customElements.get("maintenance-task-detail-view")) {
customElements.define("maintenance-task-detail-view", MaintenanceTaskDetailView);
}
@@ -88,6 +88,12 @@ function weekdayNames(lang?: string): string[] {
return Array.from({ length: 7 }, (_, i) => weekdayName(i, lang, "short"));
}
/** Short localized month names, indexed 0=Jan … 11=Dec. */
function monthNames(lang?: string): string[] {
const fmt = new Intl.DateTimeFormat(lang || "en", { month: "short" });
return Array.from({ length: 12 }, (_, i) => fmt.format(new Date(2021, i, 1)));
}
export class MaintenanceTaskDialog extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean, attribute: "checklists-enabled" }) public checklistsEnabled = false;
@@ -122,6 +128,12 @@ export class MaintenanceTaskDialog extends LitElement {
@state() private _domLastDay = false;
@state() private _domBusiness = false;
@state() private _calOffset = "0";
// Recurrence extras (apply to interval + calendar kinds): a seasonal active
// window and a finite-series end condition.
@state() private _seasonMonths: number[] = [];
@state() private _endsMode: "never" | "count" | "until" = "never";
@state() private _endsCount = "";
@state() private _endsUntil = "";
@state() private _notes = "";
@state() private _documentationUrl = "";
@state() private _customIcon = "";
@@ -245,6 +257,16 @@ export class MaintenanceTaskDialog extends LitElement {
this._domLastDay = sched?.kind === "day_of_month" && sched.day === -1;
this._domBusiness = sched?.kind === "day_of_month" && sched.business === true;
this._calOffset = sched?.offset ? String(sched.offset) : "0";
// Recurrence extras — read from the nested schedule wherever it lives.
this._seasonMonths = Array.isArray(sched?.season_months) ? [...sched!.season_months] : [];
const ends = sched?.ends;
if (ends && typeof ends.count === "number") {
this._endsMode = "count"; this._endsCount = String(ends.count); this._endsUntil = "";
} else if (ends && typeof ends.until === "string") {
this._endsMode = "until"; this._endsUntil = ends.until; this._endsCount = "";
} else {
this._endsMode = "never"; this._endsCount = ""; this._endsUntil = "";
}
this._warningDays = task.warning_days.toString();
this._earliestCompletionDays =
task.earliest_completion_days != null ? String(task.earliest_completion_days) : "";
@@ -345,6 +367,10 @@ export class MaintenanceTaskDialog extends LitElement {
this._domLastDay = false;
this._domBusiness = false;
this._calOffset = "0";
this._seasonMonths = [];
this._endsMode = "never";
this._endsCount = "";
this._endsUntil = "";
this._notes = "";
this._documentationUrl = "";
this._customIcon = "";
@@ -700,7 +726,7 @@ export class MaintenanceTaskDialog extends LitElement {
} else if (CALENDAR_KINDS.includes(this._scheduleType)) {
// Calendar kinds are sent as the nested schedule; the backend prefers
// it over the flat fields and clears any stale interval/due_date.
data.schedule = this._buildSchedule();
data.schedule = { ...this._buildSchedule(), ...this._recurrenceExtras() };
data.interval_days = null;
if (this._taskId) data.due_date = null;
} else {
@@ -710,6 +736,14 @@ export class MaintenanceTaskDialog extends LitElement {
data.interval_days = parseInt(this._intervalDays, 10);
data.interval_unit = this._intervalUnit;
data.interval_anchor = this._intervalAnchor;
// Season / finite-series ride a minimal nested schedule alongside the
// flat interval fields; the backend carries them onto the interval it
// derives from the flat fields (they can't be expressed flatly).
// Always send an authoritative nested schedule for interval tasks so
// an edit that clears season/ends actually clears them.
if (this._scheduleType === "time_based") {
data.schedule = { kind: "interval", ...this._recurrenceExtras() };
}
} else if (this._taskId) {
data.interval_days = null;
data.interval_anchor = "completion";
@@ -1133,6 +1167,72 @@ export class MaintenanceTaskDialog extends LitElement {
return withOffset(schedule);
}
/** The season/finite-series extras to attach to a recurring schedule. Empty
* values are omitted, so a clean schedule stays clean (and, since the sent
* schedule is authoritative, an omitted extra clears a previously-set one). */
private _recurrenceExtras(): { season_months?: number[]; ends?: { count?: number; until?: string } } {
const extras: { season_months?: number[]; ends?: { count?: number; until?: string } } = {};
if (this._seasonMonths.length) extras.season_months = [...this._seasonMonths].sort((a, b) => a - b);
if (this._endsMode === "count") {
const n = parseInt(this._endsCount, 10);
if (n >= 1) extras.ends = { count: n };
} else if (this._endsMode === "until" && this._endsUntil) {
extras.ends = { until: this._endsUntil };
}
return extras;
}
private _toggleSeasonMonth(m: number): void {
this._seasonMonths = this._seasonMonths.includes(m)
? this._seasonMonths.filter((x) => x !== m)
: [...this._seasonMonths, m];
}
/** Seasonal window + finite-series end — shown for recurring (interval +
* calendar) kinds, where both apply. */
private _renderRecurrenceExtras() {
const L = this._lang;
const recurring = this._scheduleType === "time_based" || CALENDAR_KINDS.includes(this._scheduleType);
if (!recurring) return nothing;
const months = monthNames(L);
return html`
<label class="field-label">${t("season_window_label", L)}</label>
<div class="field-help">${t("season_window_hint", L)}</div>
<div class="weekday-chips season-chips">
${months.map((name, i) => html`
<button
type="button"
class="season-chip ${this._seasonMonths.includes(i + 1) ? "selected" : ""}"
@click=${() => this._toggleSeasonMonth(i + 1)}
>${name}</button>`)}
</div>
<label class="field-label">${t("series_end_label", L)}</label>
<div class="select-row">
<select .value=${this._endsMode}
@change=${(e: Event) => (this._endsMode = (e.target as HTMLSelectElement).value as "never" | "count" | "until")}>
<option value="never" ?selected=${this._endsMode === "never"}>${t("series_end_never", L)}</option>
<option value="count" ?selected=${this._endsMode === "count"}>${t("series_end_after_count", L)}</option>
<option value="until" ?selected=${this._endsMode === "until"}>${t("series_end_until", L)}</option>
</select>
</div>
${this._endsMode === "count" ? html`
<ms-textfield
label="${t("series_end_count_label", L)}"
type="number" min="1"
.value=${this._endsCount}
@input=${(e: Event) => (this._endsCount = (e.target as HTMLInputElement).value)}
></ms-textfield>` : nothing}
${this._endsMode === "until" ? html`
<ms-textfield
label="${t("series_end_until_label", L)}"
type="date"
.value=${this._endsUntil}
@input=${(e: Event) => (this._endsUntil = (e.target as HTMLInputElement).value)}
></ms-textfield>` : nothing}
`;
}
/** Per-kind field groups for the calendar recurrence kinds. */
private _renderCalendarFields() {
const L = this._lang;
@@ -1415,6 +1515,7 @@ export class MaintenanceTaskDialog extends LitElement {
></ms-textfield>
`
: nothing}
${this._renderRecurrenceExtras()}
<ms-textfield
label="${t("warning_days", L)}"
type="number"
@@ -1772,6 +1873,20 @@ export class MaintenanceTaskDialog extends LitElement {
color: var(--text-primary-color, #fff);
border-color: var(--primary-color, #03a9f4);
}
.season-chip {
padding: 6px 10px;
border: 1px solid var(--divider-color);
border-radius: 16px;
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
font-size: 13px;
cursor: pointer;
}
.season-chip.selected {
background: var(--primary-color, #03a9f4);
color: var(--text-primary-color, #fff);
border-color: var(--primary-color, #03a9f4);
}
.error {
color: var(--error-color, #f44336);
font-size: 13px;
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Výňatek z manuálu",
"worksheet_pages": "strany",
"worksheet_printed": "Vytištěno",
"worksheet_never": "Nikdy"
"worksheet_never": "Nikdy",
"card_all_caught_up": "Vše hotovo — nic nevyžaduje pozornost",
"postpone": "Odložit",
"postpone_date_prompt": "Na jaké datum odložit tento termín?",
"postpone_date_label": "Nové datum",
"postponed": "Odloženo",
"postponed_to": "Odloženo na",
"season_window_label": "Sezónní okno (měsíce)",
"season_window_hint": "Termín jen ve vybraných měsících; data mimo sezónu se posunou na další aktivní měsíc. Žádné = celý rok.",
"series_end_label": "Končí",
"series_end_never": "Nikdy (opakuje se stále)",
"series_end_after_count": "Po počtu opakování",
"series_end_until": "K datu",
"series_end_count_label": "Počet opakování",
"series_end_until_label": "Datum konce"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Uddrag af manualen",
"worksheet_pages": "sider",
"worksheet_printed": "Udskrevet",
"worksheet_never": "Aldrig"
"worksheet_never": "Aldrig",
"card_all_caught_up": "Alt er klaret — intet kræver opmærksomhed",
"postpone": "Udskyd",
"postpone_date_prompt": "Udskyd denne forekomst til hvilken dato?",
"postpone_date_label": "Ny forfaldsdato",
"postponed": "Udskudt",
"postponed_to": "Udskudt til",
"season_window_label": "Sæsonvindue (måneder)",
"season_window_hint": "Kun forfalden i de valgte måneder; datoer uden for sæsonen rykkes til næste aktive måned. Ingen = hele året.",
"series_end_label": "Slutter",
"series_end_never": "Aldrig (gentages uendeligt)",
"series_end_after_count": "Efter et antal gange",
"series_end_until": "På en dato",
"series_end_count_label": "Antal gange",
"series_end_until_label": "Slutdato"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Handbuch-Auszug",
"worksheet_pages": "Seiten",
"worksheet_printed": "Gedruckt",
"worksheet_never": "Nie"
"worksheet_never": "Nie",
"card_all_caught_up": "Alles erledigt — nichts braucht Aufmerksamkeit",
"postpone": "Verschieben",
"postpone_date_prompt": "Diesen Termin auf welches Datum verschieben?",
"postpone_date_label": "Neues Fälligkeitsdatum",
"postponed": "Verschoben",
"postponed_to": "Verschoben auf",
"season_window_label": "Saisonfenster (Monate)",
"season_window_hint": "Nur in den gewählten Monaten fällig; Termine außerhalb rollen in den nächsten aktiven Monat. Keine = ganzjährig.",
"series_end_label": "Endet",
"series_end_never": "Nie (wiederholt unbegrenzt)",
"series_end_after_count": "Nach Anzahl",
"series_end_until": "An einem Datum",
"series_end_count_label": "Anzahl",
"series_end_until_label": "Enddatum"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Manual excerpt",
"worksheet_pages": "pages",
"worksheet_printed": "Printed",
"worksheet_never": "Never"
"worksheet_never": "Never",
"card_all_caught_up": "All caught up — 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"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Extracto del manual",
"worksheet_pages": "páginas",
"worksheet_printed": "Impreso",
"worksheet_never": "Nunca"
"worksheet_never": "Nunca",
"card_all_caught_up": "Todo al día — nada requiere atención",
"postpone": "Posponer",
"postpone_date_prompt": "¿A qué fecha posponer esta vez?",
"postpone_date_label": "Nueva fecha de vencimiento",
"postponed": "Pospuesto",
"postponed_to": "Pospuesto hasta",
"season_window_label": "Ventana estacional (meses)",
"season_window_hint": "Solo vence en los meses seleccionados; las fechas fuera de temporada pasan al siguiente mes activo. Ninguno = todo el año.",
"series_end_label": "Termina",
"series_end_never": "Nunca (se repite indefinidamente)",
"series_end_after_count": "Tras un número de veces",
"series_end_until": "En una fecha",
"series_end_count_label": "Número de veces",
"series_end_until_label": "Fecha de fin"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Ote käyttöohjeesta",
"worksheet_pages": "sivut",
"worksheet_printed": "Tulostettu",
"worksheet_never": "Ei koskaan"
"worksheet_never": "Ei koskaan",
"card_all_caught_up": "Kaikki kunnossa — mikään ei vaadi huomiota",
"postpone": "Lykkää",
"postpone_date_prompt": "Mihin päivään tämä kerta lykätään?",
"postpone_date_label": "Uusi eräpäivä",
"postponed": "Lykätty",
"postponed_to": "Lykätty päivään",
"season_window_label": "Kausi-ikkuna (kuukaudet)",
"season_window_hint": "Erääntyy vain valittuina kuukausina; kauden ulkopuoliset päivät siirtyvät seuraavaan aktiiviseen kuukauteen. Ei mitään = koko vuosi.",
"series_end_label": "Päättyy",
"series_end_never": "Ei koskaan (toistuu loputtomasti)",
"series_end_after_count": "Tietyn kertamäärän jälkeen",
"series_end_until": "Päivämääränä",
"series_end_count_label": "Kertojen määrä",
"series_end_until_label": "Päättymispäivä"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Extrait du manuel",
"worksheet_pages": "pages",
"worksheet_printed": "Imprimé",
"worksheet_never": "Jamais"
"worksheet_never": "Jamais",
"card_all_caught_up": "Tout est à jour — rien ne requiert d'attention",
"postpone": "Reporter",
"postpone_date_prompt": "Reporter cette échéance à quelle date ?",
"postpone_date_label": "Nouvelle date d’échéance",
"postponed": "Reporté",
"postponed_to": "Reporté au",
"season_window_label": "Fenêtre saisonnière (mois)",
"season_window_hint": "Dû uniquement les mois sélectionnés ; les dates hors saison passent au mois actif suivant. Aucun = toute l’année.",
"series_end_label": "Se termine",
"series_end_never": "Jamais (répète indéfiniment)",
"series_end_after_count": "Après un nombre de fois",
"series_end_until": "À une date",
"series_end_count_label": "Nombre de fois",
"series_end_until_label": "Date de fin"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "मैनुअल अंश",
"worksheet_pages": "पृष्ठ",
"worksheet_printed": "मुद्रित",
"worksheet_never": "कभी नहीं"
"worksheet_never": "कभी नहीं",
"card_all_caught_up": "सब पूरा — किसी चीज़ पर ध्यान देने की ज़रूरत नहीं",
"postpone": "टालें",
"postpone_date_prompt": "इस बार को किस तारीख तक टालें?",
"postpone_date_label": "नई नियत तिथि",
"postponed": "टाल दिया गया",
"postponed_to": "तक टाला गया",
"season_window_label": "मौसमी अवधि (महीने)",
"season_window_hint": "केवल चुने गए महीनों में देय; ऑफ-सीज़न तिथियाँ अगले सक्रिय महीने में चली जाती हैं। कोई नहीं = पूरे वर्ष।",
"series_end_label": "समाप्त होता है",
"series_end_never": "कभी नहीं (अनिश्चित रूप से दोहराता है)",
"series_end_after_count": "कुछ बार के बाद",
"series_end_until": "एक तिथि पर",
"series_end_count_label": "कितनी बार",
"series_end_until_label": "समाप्ति तिथि"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Estratto del manuale",
"worksheet_pages": "pagine",
"worksheet_printed": "Stampato",
"worksheet_never": "Mai"
"worksheet_never": "Mai",
"card_all_caught_up": "Tutto in ordine — niente richiede attenzione",
"postpone": "Posticipa",
"postpone_date_prompt": "A quale data posticipare questa scadenza?",
"postpone_date_label": "Nuova data di scadenza",
"postponed": "Posticipato",
"postponed_to": "Posticipato al",
"season_window_label": "Finestra stagionale (mesi)",
"season_window_hint": "Scade solo nei mesi selezionati; le date fuori stagione passano al mese attivo successivo. Nessuno = tutto l’anno.",
"series_end_label": "Termina",
"series_end_never": "Mai (ripete all’infinito)",
"series_end_after_count": "Dopo un numero di volte",
"series_end_until": "In una data",
"series_end_count_label": "Numero di volte",
"series_end_until_label": "Data di fine"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "マニュアル抜粋",
"worksheet_pages": "ページ",
"worksheet_printed": "印刷日",
"worksheet_never": "未実施"
"worksheet_never": "未実施",
"card_all_caught_up": "すべて完了 — 対応が必要なものはありません",
"postpone": "延期",
"postpone_date_prompt": "この回をどの日付に延期しますか?",
"postpone_date_label": "新しい期日",
"postponed": "延期済み",
"postponed_to": "延期先",
"season_window_label": "季節ウィンドウ(月)",
"season_window_hint": "選択した月のみ期限;シーズン外の日付は次の有効な月へ繰り越し。なし=通年。",
"series_end_label": "終了",
"series_end_never": "なし(無期限に繰り返す)",
"series_end_after_count": "回数の後",
"series_end_until": "日付で",
"series_end_count_label": "回数",
"series_end_until_label": "終了日"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Utdrag fra manualen",
"worksheet_pages": "sider",
"worksheet_printed": "Skrevet ut",
"worksheet_never": "Aldri"
"worksheet_never": "Aldri",
"card_all_caught_up": "Alt er i orden — ingenting trenger oppmerksomhet",
"postpone": "Utsett",
"postpone_date_prompt": "Utsett denne forekomsten til hvilken dato?",
"postpone_date_label": "Ny forfallsdato",
"postponed": "Utsatt",
"postponed_to": "Utsatt til",
"season_window_label": "Sesongvindu (måneder)",
"season_window_hint": "Forfaller kun i valgte måneder; datoer utenfor sesongen flyttes til neste aktive måned. Ingen = hele året.",
"series_end_label": "Slutter",
"series_end_never": "Aldri (gjentas uendelig)",
"series_end_after_count": "Etter et antall ganger",
"series_end_until": "På en dato",
"series_end_count_label": "Antall ganger",
"series_end_until_label": "Sluttdato"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Handleidingfragment",
"worksheet_pages": "pagina's",
"worksheet_printed": "Afgedrukt",
"worksheet_never": "Nooit"
"worksheet_never": "Nooit",
"card_all_caught_up": "Alles bijgewerkt — niets vraagt aandacht",
"postpone": "Uitstellen",
"postpone_date_prompt": "Deze keer uitstellen tot welke datum?",
"postpone_date_label": "Nieuwe vervaldatum",
"postponed": "Uitgesteld",
"postponed_to": "Uitgesteld tot",
"season_window_label": "Seizoensvenster (maanden)",
"season_window_hint": "Alleen verschuldigd in de gekozen maanden; data buiten het seizoen schuiven naar de volgende actieve maand. Geen = hele jaar.",
"series_end_label": "Eindigt",
"series_end_never": "Nooit (herhaalt onbeperkt)",
"series_end_after_count": "Na een aantal keer",
"series_end_until": "Op een datum",
"series_end_count_label": "Aantal keer",
"series_end_until_label": "Einddatum"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Fragment instrukcji",
"worksheet_pages": "strony",
"worksheet_printed": "Wydrukowano",
"worksheet_never": "Nigdy"
"worksheet_never": "Nigdy",
"card_all_caught_up": "Wszystko zrobione — nic nie wymaga uwagi",
"postpone": "Odłóż",
"postpone_date_prompt": "Przełożyć ten termin na jaką datę?",
"postpone_date_label": "Nowa data",
"postponed": "Odłożono",
"postponed_to": "Odłożono do",
"season_window_label": "Okno sezonowe (miesiące)",
"season_window_hint": "Termin tylko w wybranych miesiącach; daty poza sezonem przechodzą na następny aktywny miesiąc. Brak = cały rok.",
"series_end_label": "Kończy się",
"series_end_never": "Nigdy (powtarza bez końca)",
"series_end_after_count": "Po liczbie razy",
"series_end_until": "W dniu",
"series_end_count_label": "Liczba razy",
"series_end_until_label": "Data końcowa"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Excerto do manual",
"worksheet_pages": "páginas",
"worksheet_printed": "Impresso",
"worksheet_never": "Nunca"
"worksheet_never": "Nunca",
"card_all_caught_up": "Tudo em dia — nada requer atenção",
"postpone": "Adiar",
"postpone_date_prompt": "Adiar esta ocorrência para qual data?",
"postpone_date_label": "Nova data de vencimento",
"postponed": "Adiado",
"postponed_to": "Adiado para",
"season_window_label": "Janela sazonal (meses)",
"season_window_hint": "Só vence nos meses selecionados; datas fora de época passam para o próximo mês ativo. Nenhum = todo o ano.",
"series_end_label": "Termina",
"series_end_never": "Nunca (repete indefinidamente)",
"series_end_after_count": "Após um número de vezes",
"series_end_until": "Numa data",
"series_end_count_label": "Número de vezes",
"series_end_until_label": "Data final"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Выдержка из руководства",
"worksheet_pages": "страницы",
"worksheet_printed": "Напечатано",
"worksheet_never": "Никогда"
"worksheet_never": "Никогда",
"card_all_caught_up": "Всё выполнено — ничего не требует внимания",
"postpone": "Отложить",
"postpone_date_prompt": "На какую дату отложить этот раз?",
"postpone_date_label": "Новый срок",
"postponed": "Отложено",
"postponed_to": "Отложено до",
"season_window_label": "Сезонное окно (месяцы)",
"season_window_hint": "Срок только в выбранные месяцы; даты вне сезона переносятся на следующий активный месяц. Нет = весь год.",
"series_end_label": "Заканчивается",
"series_end_never": "Никогда (повторяется бесконечно)",
"series_end_after_count": "После числа раз",
"series_end_until": "В дату",
"series_end_count_label": "Число раз",
"series_end_until_label": "Дата окончания"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Utdrag ur manualen",
"worksheet_pages": "sidor",
"worksheet_printed": "Utskriven",
"worksheet_never": "Aldrig"
"worksheet_never": "Aldrig",
"card_all_caught_up": "Allt klart — inget behöver åtgärdas",
"postpone": "Skjut upp",
"postpone_date_prompt": "Skjut upp den här gången till vilket datum?",
"postpone_date_label": "Nytt förfallodatum",
"postponed": "Uppskjuten",
"postponed_to": "Uppskjuten till",
"season_window_label": "Säsongsfönster (månader)",
"season_window_hint": "Förfaller endast valda månader; datum utanför säsongen flyttas till nästa aktiva månad. Inga = hela året.",
"series_end_label": "Slutar",
"series_end_never": "Aldrig (upprepas i oändlighet)",
"series_end_after_count": "Efter ett antal gånger",
"series_end_until": "På ett datum",
"series_end_count_label": "Antal gånger",
"series_end_until_label": "Slutdatum"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Витяг з посібника",
"worksheet_pages": "сторінки",
"worksheet_printed": "Надруковано",
"worksheet_never": "Ніколи"
"worksheet_never": "Ніколи",
"card_all_caught_up": "Усе виконано — ніщо не потребує уваги",
"postpone": "Відкласти",
"postpone_date_prompt": "На яку дату відкласти цей раз?",
"postpone_date_label": "Новий термін",
"postponed": "Відкладено",
"postponed_to": "Відкладено до",
"season_window_label": "Сезонне вікно (місяці)",
"season_window_hint": "Термін лише у вибрані місяці; дати поза сезоном переносяться на наступний активний місяць. Немає = весь рік.",
"series_end_label": "Завершується",
"series_end_never": "Ніколи (повторюється безкінечно)",
"series_end_after_count": "Після кількості разів",
"series_end_until": "У дату",
"series_end_count_label": "Кількість разів",
"series_end_until_label": "Дата завершення"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "手册摘录",
"worksheet_pages": "页",
"worksheet_printed": "打印于",
"worksheet_never": "从未"
"worksheet_never": "从未",
"card_all_caught_up": "全部完成——没有需要处理的事项",
"postpone": "推迟",
"postpone_date_prompt": "将此次推迟到哪一天?",
"postpone_date_label": "新的到期日",
"postponed": "已推迟",
"postponed_to": "推迟至",
"season_window_label": "季节窗口(月份)",
"season_window_hint": "仅在所选月份到期;非当季日期顺延至下一个活动月份。无 = 全年。",
"series_end_label": "结束",
"series_end_never": "从不(无限重复)",
"series_end_after_count": "达到次数后",
"series_end_until": "在某日期",
"series_end_count_label": "次数",
"series_end_until_label": "结束日期"
}
@@ -249,10 +249,16 @@ export class MaintenanceSupporterCard extends LitElement {
</div>
</div>
${tasks.length === 0
? html`<div class="empty-card">
<div>${t("card_no_tasks_title", L)}</div>
<a class="empty-link" href="/maintenance-supporter">${t("card_no_tasks_cta", L)}</a>
</div>`
? this._objects.some((o) => o.tasks.length > 0)
? html`<div class="empty-card">
<!-- (#86) tasks exist but none match the filter (default:
actionable-only) — "all caught up", NOT "no tasks yet". -->
<div class="all-caught-up">✓ ${t("card_all_caught_up", L)}</div>
</div>`
: html`<div class="empty-card">
<div>${t("card_no_tasks_title", L)}</div>
<a class="empty-link" href="/maintenance-supporter">${t("card_no_tasks_cta", L)}</a>
</div>`
: html`
<div class="task-list ${compact ? "compact" : ""}">
${tasks.map(
@@ -262,7 +268,16 @@ export class MaintenanceSupporterCard extends LitElement {
title="${t("open_task", L) || "Open task"}">
<div class="status-dot" style="background: ${STATUS_COLORS[task.status] || "#ccc"}"></div>
<div class="task-info">
<div class="task-name">${task.name}</div>
<div class="task-name">
${task.name}
${(task as any).due_override
? html`<ha-icon
class="postponed-icon"
icon="mdi:calendar-clock"
title="${t("postponed", L) || "Postponed"}"
></ha-icon>`
: nothing}
</div>
${!compact ? html`<div class="task-meta">${object_name} · ${t(task.type, L)}</div>` : nothing}
</div>
<div class="task-due">
@@ -366,6 +381,7 @@ export class MaintenanceSupporterCard extends LitElement {
font-size: 13px;
}
.empty-link:hover { text-decoration: underline; }
.all-caught-up { color: var(--success-color, #4caf50); font-weight: 500; }
.task-list { padding: 0 16px 16px; }
.task-item {
@@ -387,6 +403,7 @@ export class MaintenanceSupporterCard extends LitElement {
.status-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; }
.task-info { flex: 1; min-width: 0; }
.task-name { font-size: 14px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.postponed-icon { --mdc-icon-size: 14px; color: var(--secondary-text-color); vertical-align: text-bottom; margin-inline-start: 4px; }
.task-meta { font-size: 12px; color: var(--secondary-text-color); }
.task-due { font-size: 13px; color: var(--secondary-text-color); min-width: 40px; text-align: right; }
@@ -200,15 +200,22 @@ const SMALL_SCREEN_CONDITION: ViewColumnsCondition = {
// truth shared with the panel KPI chips and the statistics WS endpoint — so the
// numbers can't drift. Rendered live via a markdown template instead of being
// counted client-side at generation time (which froze them until reload).
function kpiMarkdownCard(): CardConfig {
// (#86) Entity ids come registry-resolved from the statistics WS: the
// documented sensor.maintenance_supporter_<key> ids are NOT guaranteed —
// a user rename, a _2 collision suffix, or a pre-pinning install that
// registered localized ids all made the hardcoded templates read "unknown".
type SummaryEntityIds = Partial<Record<"overdue" | "triggered" | "due_soon" | "ok", string | null>>;
function kpiMarkdownCard(ids: SummaryEntityIds = {}): CardConfig {
const eid = (key: keyof SummaryEntityIds) => ids[key] || `sensor.maintenance_supporter_${key}`;
return {
type: "markdown",
text_only: true,
content: [
"🔴 **{{ states('sensor.maintenance_supporter_overdue') }}** overdue",
"⚡ **{{ states('sensor.maintenance_supporter_triggered') }}** triggered",
"🟡 **{{ states('sensor.maintenance_supporter_due_soon') }}** due soon",
"🟢 **{{ states('sensor.maintenance_supporter_ok') }}** ok",
`🔴 **{{ states('${eid("overdue")}') }}** overdue`,
`⚡ **{{ states('${eid("triggered")}') }}** triggered`,
`🟡 **{{ states('${eid("due_soon")}') }}** due soon`,
`🟢 **{{ states('${eid("ok")}') }}** ok`,
].join(" · "),
};
}
@@ -263,8 +270,8 @@ function emptyStateView(): ViewConfig {
};
}
function overviewView(): ViewConfig {
const kpiCard = kpiMarkdownCard();
function overviewView(summaryIds: SummaryEntityIds = {}): ViewConfig {
const kpiCard = kpiMarkdownCard(summaryIds);
return {
title: "Overview",
icon: "mdi:wrench-clock",
@@ -614,10 +621,19 @@ export class MaintenanceDashboardStrategy extends HTMLElement {
calendar: () => viewsByCalendar(objects),
};
// Registry-resolved summary-sensor ids (#86); tolerate older backends.
let summaryIds: SummaryEntityIds = {};
try {
const stats = await hass.connection.sendMessagePromise<{ summary_entity_ids?: SummaryEntityIds }>({
type: "maintenance_supporter/statistics",
});
summaryIds = stats.summary_entity_ids || {};
} catch { /* fall back to the documented default ids */ }
return {
title: "Maintenance",
views: [
overviewView(),
overviewView(summaryIds),
...(viewBuilders[groupBy] ?? viewBuilders.area)(),
],
};
@@ -57,7 +57,11 @@ import { type SparklineContext } from "./renderers/sparkline";
import { buildCalendarBuckets, isoDateLocal, type CalendarEvent } from "./helpers/calendar-bucket";
import { renderTriggerProgress, renderMiniSparkline } from "./renderers/progress";
import { type HistoryContext } from "./renderers/history";
import { renderTaskDetail, renderUserBadge, type TaskDetailContext } from "./renderers/task-detail";
import { renderUserBadge, type TaskDetailContext } from "./renderers/task-detail";
// The task-detail sub-view as a web component (light-DOM, panel styles apply).
// Side-effect import: type-only imports get tree-shaken and the element
// would never register.
import "./components/task-detail-view";
import { computeWindow, VIRTUAL_MIN_ROWS } from "./helpers/virtual-window";
type View = "overview" | "object" | "task" | "all_objects";
@@ -1392,6 +1396,38 @@ export class MaintenanceSupporterPanel extends LitElement {
this._resetTask(entryId, taskId, result.value || undefined);
}
private async _postponeTask(entryId: string, taskId: string, until: string): Promise<void> {
this._actionLoading = true;
try {
await this.hass.connection.sendMessagePromise({
type: "maintenance_supporter/task/postpone",
entry_id: entryId,
task_id: taskId,
until,
});
this._showToast(t("postponed", this._lang));
await this._loadData();
} catch {
this._showToast(t("action_error", this._lang));
} finally {
this._actionLoading = false;
}
}
private async _promptPostponeTask(entryId: string, taskId: string): Promise<void> {
const dlg = this.shadowRoot!.querySelector<MaintenanceConfirmDialog>("maintenance-confirm-dialog");
if (!dlg) return;
const result = await dlg.prompt({
title: t("postpone", this._lang),
message: t("postpone_date_prompt", this._lang),
confirmText: t("postpone", this._lang),
inputLabel: t("postpone_date_label", this._lang),
inputType: "date",
});
if (!result.confirmed || !result.value) return;
this._postponeTask(entryId, taskId, result.value);
}
private async _snoozeTask(entryId: string, taskId: string): Promise<void> {
this._actionLoading = true;
try {
@@ -2855,6 +2891,7 @@ export class MaintenanceSupporterPanel extends LitElement {
openQr: (taskName) => this._openQrForTask(entryId, taskId, obj?.object.name || "", taskName),
duplicateTask: () => this._duplicateTask(entryId, taskId),
promptReset: () => this._promptResetTask(entryId, taskId),
promptPostpone: () => this._promptPostponeTask(entryId, taskId),
snoozeTask: () => this._snoozeTask(entryId, taskId),
printWorksheet: () => this._printTaskWorksheet(entryId, taskId),
deleteTask: () => this._deleteTask(entryId, taskId),
@@ -2869,7 +2906,10 @@ export class MaintenanceSupporterPanel extends LitElement {
if (!this._selectedEntryId || !this._selectedTaskId) return nothing;
const task = this._getTask(this._selectedEntryId, this._selectedTaskId);
if (!task) return html`<p>Task not found.</p>`;
return renderTaskDetail(task, this._taskDetailCtx());
return html`<maintenance-task-detail-view
.task=${task}
.ctx=${this._taskDetailCtx()}
></maintenance-task-detail-view>`;
}
/** v2.2.0: open the in-place history-edit dialog for the given entry.
@@ -304,6 +304,10 @@ export const panelStyles = css`
--mdc-icon-size: 26px;
}
/* Custom elements default to display:inline; the task-detail component
renders light-DOM and must behave like the block it wraps. */
maintenance-task-detail-view { display: block; }
.detail-section { padding: 16px 0; }
.detail-header {
@@ -589,9 +593,29 @@ export const panelStyles = css`
border-radius: 12px;
padding: 2px;
}
/* Inside .cell-badges the parent's gap does the spacing — the badges' own
margin-left (meant for inline use, e.g. the detail header) would double
it and push the priority chevron out of line with the other badges. */
.cell-badges .nfc-badge,
.cell-badges .priority-badge {
margin-left: 0;
}
.priority-badge ha-icon {
--mdc-icon-size: 16px;
}
.postponed-badge {
display: inline-flex;
align-items: center;
gap: 3px;
padding: 2px 8px;
margin-left: 8px;
background: var(--secondary-background-color, #e8e8e8);
color: var(--secondary-text-color);
border-radius: 10px;
font-size: 11px;
font-weight: 500;
}
.postponed-badge ha-icon { --mdc-icon-size: 13px; }
.priority-high {
color: var(--error-color, #db4437);
}
@@ -61,6 +61,7 @@ export interface TaskDetailContext {
openQr: (taskName: string) => void;
duplicateTask: () => void;
promptReset: () => void;
promptPostpone: () => void;
snoozeTask: () => void;
/** v2.21: open the printable one-pager for this task. */
printWorksheet: () => void;
@@ -104,6 +105,9 @@ function renderTaskHeader(task: MaintenanceTask, ctx: TaskDetailContext) {
<span class="breadcrumb-separator">·</span>
<span class="object-name-breadcrumb" @click=${() => ctx.showObject()}>${ctx.objectName}</span>
<span class="status-chip ${statusClass}">${statusText}</span>
${task.due_override ? html`<span class="postponed-badge" title="${t("postponed_to", L)}">
<ha-icon icon="mdi:calendar-arrow-right"></ha-icon>${formatDate(task.due_override, L)}
</span>` : nothing}
${renderUserBadge(task, ctx.getUserName)}
${task.nfc_tag_id
? html`<span class="nfc-badge" title="${t("nfc_tag_id", L)}: ${task.nfc_tag_id}"><ha-icon icon="mdi:nfc-variant"></ha-icon> NFC</span>`
@@ -131,6 +135,7 @@ function renderTaskHeader(task: MaintenanceTask, ctx: TaskDetailContext) {
<div class="popup-menu-item" @click=${() => { ctx.closeMoreMenu(); ctx.openEdit(task); }}>${t("edit", L)}</div>
<div class="popup-menu-item" @click=${() => ctx.duplicateTask()}>${t("duplicate", L)}</div>
<div class="popup-menu-item" @click=${() => { ctx.closeMoreMenu(); ctx.promptReset(); }}>${t("reset", L)}</div>
<div class="popup-menu-item" @click=${() => { ctx.closeMoreMenu(); ctx.promptPostpone(); }}>${t("postpone", L)}…</div>
<div class="popup-menu-item" @click=${() => { ctx.closeMoreMenu(); ctx.snoozeTask(); }}>${t("snooze", L)}</div>
<div class="popup-menu-item" @click=${() => { ctx.closeMoreMenu(); ctx.printWorksheet(); }}>${t("worksheet", L)}</div>
<div class="popup-menu-divider"></div>
@@ -740,11 +740,14 @@ export const sharedStyles = css`
transition: width 0.3s;
}
/* Trigger progress bar (overview rows) */
/* Trigger progress bar (overview rows). width:100% — the due-cell doesn't
stretch its children (align-items: flex-end), so without it the bar
shrinks to its label and reads shorter than the days-bar in other rows. */
.trigger-progress {
display: flex;
flex-direction: column;
gap: 2px;
width: 100%;
min-width: 90px;
}
@@ -133,6 +133,10 @@ export interface TaskSchedule {
business?: boolean;
/** (#83) shift the computed occurrence by ±N days (clamped ±15). */
offset?: number;
/** Seasonal active window — months (1..12) the task may be due in. */
season_months?: number[];
/** Finite-series end condition: after N completions and/or past a date. */
ends?: { count?: number; until?: string };
}
export interface MaintenanceTask {
@@ -182,6 +186,8 @@ export interface MaintenanceTask {
archived_reason?: string | null;
days_until_due?: number | null;
next_due?: string | null;
/** Per-occurrence postpone: the ISO date the current cycle was deferred to. */
due_override?: string | null;
trigger_active: boolean;
trigger_current_value?: number | null;
trigger_current_delta?: number | null;
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Výňatek z manuálu",
"worksheet_pages": "strany",
"worksheet_printed": "Vytištěno",
"worksheet_never": "Nikdy"
"worksheet_never": "Nikdy",
"card_all_caught_up": "Vše hotovo — nic nevyžaduje pozornost",
"postpone": "Odložit",
"postpone_date_prompt": "Na jaké datum odložit tento termín?",
"postpone_date_label": "Nové datum",
"postponed": "Odloženo",
"postponed_to": "Odloženo na",
"season_window_label": "Sezónní okno (měsíce)",
"season_window_hint": "Termín jen ve vybraných měsících; data mimo sezónu se posunou na další aktivní měsíc. Žádné = celý rok.",
"series_end_label": "Končí",
"series_end_never": "Nikdy (opakuje se stále)",
"series_end_after_count": "Po počtu opakování",
"series_end_until": "K datu",
"series_end_count_label": "Počet opakování",
"series_end_until_label": "Datum konce"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Uddrag af manualen",
"worksheet_pages": "sider",
"worksheet_printed": "Udskrevet",
"worksheet_never": "Aldrig"
"worksheet_never": "Aldrig",
"card_all_caught_up": "Alt er klaret — intet kræver opmærksomhed",
"postpone": "Udskyd",
"postpone_date_prompt": "Udskyd denne forekomst til hvilken dato?",
"postpone_date_label": "Ny forfaldsdato",
"postponed": "Udskudt",
"postponed_to": "Udskudt til",
"season_window_label": "Sæsonvindue (måneder)",
"season_window_hint": "Kun forfalden i de valgte måneder; datoer uden for sæsonen rykkes til næste aktive måned. Ingen = hele året.",
"series_end_label": "Slutter",
"series_end_never": "Aldrig (gentages uendeligt)",
"series_end_after_count": "Efter et antal gange",
"series_end_until": "På en dato",
"series_end_count_label": "Antal gange",
"series_end_until_label": "Slutdato"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Handbuch-Auszug",
"worksheet_pages": "Seiten",
"worksheet_printed": "Gedruckt",
"worksheet_never": "Nie"
"worksheet_never": "Nie",
"card_all_caught_up": "Alles erledigt — nichts braucht Aufmerksamkeit",
"postpone": "Verschieben",
"postpone_date_prompt": "Diesen Termin auf welches Datum verschieben?",
"postpone_date_label": "Neues Fälligkeitsdatum",
"postponed": "Verschoben",
"postponed_to": "Verschoben auf",
"season_window_label": "Saisonfenster (Monate)",
"season_window_hint": "Nur in den gewählten Monaten fällig; Termine außerhalb rollen in den nächsten aktiven Monat. Keine = ganzjährig.",
"series_end_label": "Endet",
"series_end_never": "Nie (wiederholt unbegrenzt)",
"series_end_after_count": "Nach Anzahl",
"series_end_until": "An einem Datum",
"series_end_count_label": "Anzahl",
"series_end_until_label": "Enddatum"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Manual excerpt",
"worksheet_pages": "pages",
"worksheet_printed": "Printed",
"worksheet_never": "Never"
"worksheet_never": "Never",
"card_all_caught_up": "All caught up — 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"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Extracto del manual",
"worksheet_pages": "páginas",
"worksheet_printed": "Impreso",
"worksheet_never": "Nunca"
"worksheet_never": "Nunca",
"card_all_caught_up": "Todo al día — nada requiere atención",
"postpone": "Posponer",
"postpone_date_prompt": "¿A qué fecha posponer esta vez?",
"postpone_date_label": "Nueva fecha de vencimiento",
"postponed": "Pospuesto",
"postponed_to": "Pospuesto hasta",
"season_window_label": "Ventana estacional (meses)",
"season_window_hint": "Solo vence en los meses seleccionados; las fechas fuera de temporada pasan al siguiente mes activo. Ninguno = todo el año.",
"series_end_label": "Termina",
"series_end_never": "Nunca (se repite indefinidamente)",
"series_end_after_count": "Tras un número de veces",
"series_end_until": "En una fecha",
"series_end_count_label": "Número de veces",
"series_end_until_label": "Fecha de fin"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Ote käyttöohjeesta",
"worksheet_pages": "sivut",
"worksheet_printed": "Tulostettu",
"worksheet_never": "Ei koskaan"
"worksheet_never": "Ei koskaan",
"card_all_caught_up": "Kaikki kunnossa — mikään ei vaadi huomiota",
"postpone": "Lykkää",
"postpone_date_prompt": "Mihin päivään tämä kerta lykätään?",
"postpone_date_label": "Uusi eräpäivä",
"postponed": "Lykätty",
"postponed_to": "Lykätty päivään",
"season_window_label": "Kausi-ikkuna (kuukaudet)",
"season_window_hint": "Erääntyy vain valittuina kuukausina; kauden ulkopuoliset päivät siirtyvät seuraavaan aktiiviseen kuukauteen. Ei mitään = koko vuosi.",
"series_end_label": "Päättyy",
"series_end_never": "Ei koskaan (toistuu loputtomasti)",
"series_end_after_count": "Tietyn kertamäärän jälkeen",
"series_end_until": "Päivämääränä",
"series_end_count_label": "Kertojen määrä",
"series_end_until_label": "Päättymispäivä"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Extrait du manuel",
"worksheet_pages": "pages",
"worksheet_printed": "Imprimé",
"worksheet_never": "Jamais"
"worksheet_never": "Jamais",
"card_all_caught_up": "Tout est à jour — rien ne requiert d'attention",
"postpone": "Reporter",
"postpone_date_prompt": "Reporter cette échéance à quelle date ?",
"postpone_date_label": "Nouvelle date d’échéance",
"postponed": "Reporté",
"postponed_to": "Reporté au",
"season_window_label": "Fenêtre saisonnière (mois)",
"season_window_hint": "Dû uniquement les mois sélectionnés ; les dates hors saison passent au mois actif suivant. Aucun = toute l’année.",
"series_end_label": "Se termine",
"series_end_never": "Jamais (répète indéfiniment)",
"series_end_after_count": "Après un nombre de fois",
"series_end_until": "À une date",
"series_end_count_label": "Nombre de fois",
"series_end_until_label": "Date de fin"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "मैनुअल अंश",
"worksheet_pages": "पृष्ठ",
"worksheet_printed": "मुद्रित",
"worksheet_never": "कभी नहीं"
"worksheet_never": "कभी नहीं",
"card_all_caught_up": "सब पूरा — किसी चीज़ पर ध्यान देने की ज़रूरत नहीं",
"postpone": "टालें",
"postpone_date_prompt": "इस बार को किस तारीख तक टालें?",
"postpone_date_label": "नई नियत तिथि",
"postponed": "टाल दिया गया",
"postponed_to": "तक टाला गया",
"season_window_label": "मौसमी अवधि (महीने)",
"season_window_hint": "केवल चुने गए महीनों में देय; ऑफ-सीज़न तिथियाँ अगले सक्रिय महीने में चली जाती हैं। कोई नहीं = पूरे वर्ष।",
"series_end_label": "समाप्त होता है",
"series_end_never": "कभी नहीं (अनिश्चित रूप से दोहराता है)",
"series_end_after_count": "कुछ बार के बाद",
"series_end_until": "एक तिथि पर",
"series_end_count_label": "कितनी बार",
"series_end_until_label": "समाप्ति तिथि"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Estratto del manuale",
"worksheet_pages": "pagine",
"worksheet_printed": "Stampato",
"worksheet_never": "Mai"
"worksheet_never": "Mai",
"card_all_caught_up": "Tutto in ordine — niente richiede attenzione",
"postpone": "Posticipa",
"postpone_date_prompt": "A quale data posticipare questa scadenza?",
"postpone_date_label": "Nuova data di scadenza",
"postponed": "Posticipato",
"postponed_to": "Posticipato al",
"season_window_label": "Finestra stagionale (mesi)",
"season_window_hint": "Scade solo nei mesi selezionati; le date fuori stagione passano al mese attivo successivo. Nessuno = tutto l’anno.",
"series_end_label": "Termina",
"series_end_never": "Mai (ripete all’infinito)",
"series_end_after_count": "Dopo un numero di volte",
"series_end_until": "In una data",
"series_end_count_label": "Numero di volte",
"series_end_until_label": "Data di fine"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "マニュアル抜粋",
"worksheet_pages": "ページ",
"worksheet_printed": "印刷日",
"worksheet_never": "未実施"
"worksheet_never": "未実施",
"card_all_caught_up": "すべて完了 — 対応が必要なものはありません",
"postpone": "延期",
"postpone_date_prompt": "この回をどの日付に延期しますか?",
"postpone_date_label": "新しい期日",
"postponed": "延期済み",
"postponed_to": "延期先",
"season_window_label": "季節ウィンドウ(月)",
"season_window_hint": "選択した月のみ期限;シーズン外の日付は次の有効な月へ繰り越し。なし=通年。",
"series_end_label": "終了",
"series_end_never": "なし(無期限に繰り返す)",
"series_end_after_count": "回数の後",
"series_end_until": "日付で",
"series_end_count_label": "回数",
"series_end_until_label": "終了日"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Utdrag fra manualen",
"worksheet_pages": "sider",
"worksheet_printed": "Skrevet ut",
"worksheet_never": "Aldri"
"worksheet_never": "Aldri",
"card_all_caught_up": "Alt er i orden — ingenting trenger oppmerksomhet",
"postpone": "Utsett",
"postpone_date_prompt": "Utsett denne forekomsten til hvilken dato?",
"postpone_date_label": "Ny forfallsdato",
"postponed": "Utsatt",
"postponed_to": "Utsatt til",
"season_window_label": "Sesongvindu (måneder)",
"season_window_hint": "Forfaller kun i valgte måneder; datoer utenfor sesongen flyttes til neste aktive måned. Ingen = hele året.",
"series_end_label": "Slutter",
"series_end_never": "Aldri (gjentas uendelig)",
"series_end_after_count": "Etter et antall ganger",
"series_end_until": "På en dato",
"series_end_count_label": "Antall ganger",
"series_end_until_label": "Sluttdato"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Handleidingfragment",
"worksheet_pages": "pagina's",
"worksheet_printed": "Afgedrukt",
"worksheet_never": "Nooit"
"worksheet_never": "Nooit",
"card_all_caught_up": "Alles bijgewerkt — niets vraagt aandacht",
"postpone": "Uitstellen",
"postpone_date_prompt": "Deze keer uitstellen tot welke datum?",
"postpone_date_label": "Nieuwe vervaldatum",
"postponed": "Uitgesteld",
"postponed_to": "Uitgesteld tot",
"season_window_label": "Seizoensvenster (maanden)",
"season_window_hint": "Alleen verschuldigd in de gekozen maanden; data buiten het seizoen schuiven naar de volgende actieve maand. Geen = hele jaar.",
"series_end_label": "Eindigt",
"series_end_never": "Nooit (herhaalt onbeperkt)",
"series_end_after_count": "Na een aantal keer",
"series_end_until": "Op een datum",
"series_end_count_label": "Aantal keer",
"series_end_until_label": "Einddatum"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Fragment instrukcji",
"worksheet_pages": "strony",
"worksheet_printed": "Wydrukowano",
"worksheet_never": "Nigdy"
"worksheet_never": "Nigdy",
"card_all_caught_up": "Wszystko zrobione — nic nie wymaga uwagi",
"postpone": "Odłóż",
"postpone_date_prompt": "Przełożyć ten termin na jaką datę?",
"postpone_date_label": "Nowa data",
"postponed": "Odłożono",
"postponed_to": "Odłożono do",
"season_window_label": "Okno sezonowe (miesiące)",
"season_window_hint": "Termin tylko w wybranych miesiącach; daty poza sezonem przechodzą na następny aktywny miesiąc. Brak = cały rok.",
"series_end_label": "Kończy się",
"series_end_never": "Nigdy (powtarza bez końca)",
"series_end_after_count": "Po liczbie razy",
"series_end_until": "W dniu",
"series_end_count_label": "Liczba razy",
"series_end_until_label": "Data końcowa"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Excerto do manual",
"worksheet_pages": "páginas",
"worksheet_printed": "Impresso",
"worksheet_never": "Nunca"
"worksheet_never": "Nunca",
"card_all_caught_up": "Tudo em dia — nada requer atenção",
"postpone": "Adiar",
"postpone_date_prompt": "Adiar esta ocorrência para qual data?",
"postpone_date_label": "Nova data de vencimento",
"postponed": "Adiado",
"postponed_to": "Adiado para",
"season_window_label": "Janela sazonal (meses)",
"season_window_hint": "Só vence nos meses selecionados; datas fora de época passam para o próximo mês ativo. Nenhum = todo o ano.",
"series_end_label": "Termina",
"series_end_never": "Nunca (repete indefinidamente)",
"series_end_after_count": "Após um número de vezes",
"series_end_until": "Numa data",
"series_end_count_label": "Número de vezes",
"series_end_until_label": "Data final"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Выдержка из руководства",
"worksheet_pages": "страницы",
"worksheet_printed": "Напечатано",
"worksheet_never": "Никогда"
"worksheet_never": "Никогда",
"card_all_caught_up": "Всё выполнено — ничего не требует внимания",
"postpone": "Отложить",
"postpone_date_prompt": "На какую дату отложить этот раз?",
"postpone_date_label": "Новый срок",
"postponed": "Отложено",
"postponed_to": "Отложено до",
"season_window_label": "Сезонное окно (месяцы)",
"season_window_hint": "Срок только в выбранные месяцы; даты вне сезона переносятся на следующий активный месяц. Нет = весь год.",
"series_end_label": "Заканчивается",
"series_end_never": "Никогда (повторяется бесконечно)",
"series_end_after_count": "После числа раз",
"series_end_until": "В дату",
"series_end_count_label": "Число раз",
"series_end_until_label": "Дата окончания"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Utdrag ur manualen",
"worksheet_pages": "sidor",
"worksheet_printed": "Utskriven",
"worksheet_never": "Aldrig"
"worksheet_never": "Aldrig",
"card_all_caught_up": "Allt klart — inget behöver åtgärdas",
"postpone": "Skjut upp",
"postpone_date_prompt": "Skjut upp den här gången till vilket datum?",
"postpone_date_label": "Nytt förfallodatum",
"postponed": "Uppskjuten",
"postponed_to": "Uppskjuten till",
"season_window_label": "Säsongsfönster (månader)",
"season_window_hint": "Förfaller endast valda månader; datum utanför säsongen flyttas till nästa aktiva månad. Inga = hela året.",
"series_end_label": "Slutar",
"series_end_never": "Aldrig (upprepas i oändlighet)",
"series_end_after_count": "Efter ett antal gånger",
"series_end_until": "På ett datum",
"series_end_count_label": "Antal gånger",
"series_end_until_label": "Slutdatum"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "Витяг з посібника",
"worksheet_pages": "сторінки",
"worksheet_printed": "Надруковано",
"worksheet_never": "Ніколи"
"worksheet_never": "Ніколи",
"card_all_caught_up": "Усе виконано — ніщо не потребує уваги",
"postpone": "Відкласти",
"postpone_date_prompt": "На яку дату відкласти цей раз?",
"postpone_date_label": "Новий термін",
"postponed": "Відкладено",
"postponed_to": "Відкладено до",
"season_window_label": "Сезонне вікно (місяці)",
"season_window_hint": "Термін лише у вибрані місяці; дати поза сезоном переносяться на наступний активний місяць. Немає = весь рік.",
"series_end_label": "Завершується",
"series_end_never": "Ніколи (повторюється безкінечно)",
"series_end_after_count": "Після кількості разів",
"series_end_until": "У дату",
"series_end_count_label": "Кількість разів",
"series_end_until_label": "Дата завершення"
}
@@ -692,5 +692,19 @@
"worksheet_manual_excerpt": "手册摘录",
"worksheet_pages": "页",
"worksheet_printed": "打印于",
"worksheet_never": "从未"
"worksheet_never": "从未",
"card_all_caught_up": "全部完成——没有需要处理的事项",
"postpone": "推迟",
"postpone_date_prompt": "将此次推迟到哪一天?",
"postpone_date_label": "新的到期日",
"postponed": "已推迟",
"postponed_to": "推迟至",
"season_window_label": "季节窗口(月份)",
"season_window_hint": "仅在所选月份到期;非当季日期顺延至下一个活动月份。无 = 全年。",
"series_end_label": "结束",
"series_end_never": "从不(无限重复)",
"series_end_after_count": "达到次数后",
"series_end_until": "在某日期",
"series_end_count_label": "次数",
"series_end_until_label": "结束日期"
}
@@ -195,7 +195,7 @@ var Xe=Object.defineProperty;var Ze=Object.getOwnPropertyDescriptor;var v=(n,e,t
}
`;var Fe={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",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:"Sel Line truncated
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:"Sel Line truncated
:host {
--maint-ok-color: var(--success-color, #4caf50);
--maint-due-soon-color: var(--warning-color, #ff9800);
@@ -670,11 +670,14 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
transition: width 0.3s;
}
/* Trigger progress bar (overview rows) */
/* Trigger progress bar (overview rows). width:100% — the due-cell doesn't
stretch its children (align-items: flex-end), so without it the bar
shrinks to its label and reads shorter than the days-bar in other rows. */
.trigger-progress {
display: flex;
flex-direction: column;
gap: 2px;
width: 100%;
min-width: 90px;
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -1,4 +1,4 @@
import{a as f}from"./chunk-6EEEWEML.js";import{a as h,b as o,d as b,e as m,f as g,g as n,h as _,j as a,l as v,s as p}from"./chunk-2LLDFX26.js";import{a as i}from"./chunk-LO2NM3CE.js";var s=class extends m{constructor(){super(...arguments);this._config={type:""};this._status=null;this._busy=!1;this._error="";this._localMonthly="";this._localYearly="";this._dirty=!1;this._loaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return this.hass?.language||"en"}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._loaded&&(this._loaded=!0,this._load(),v(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/budget_status"});this._status=t,this._localMonthly=t.monthly_budget?String(t.monthly_budget):"",this._localYearly=t.yearly_budget?String(t.yearly_budget):"",this._dirty=!1}catch(t){this._error=p(t,this._lang)}}async _save(){if(this._isAdmin){this._busy=!0,this._error="";try{let t=parseFloat(this._localMonthly),e=parseFloat(this._localYearly),r={};!isNaN(t)&&t>=0&&(r.budget_monthly=t),!isNaN(e)&&e>=0&&(r.budget_yearly=e),await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/global/update",settings:r}),await this._load()}catch(t){this._error=p(t,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_budget"),window.dispatchEvent(new CustomEvent("location-changed"))}render(){let t=this._lang,e=this._status;if(!e)return o`<ha-card><div class="loading">${a("loading",t)||"Loading\u2026"}</div></ha-card>`;let r=e.currency_symbol||_,l=e.monthly_budget?Math.min(100,(e.monthly_spent||0)/e.monthly_budget*100):0,d=e.yearly_budget?Math.min(100,(e.yearly_spent||0)/e.yearly_budget*100):0,u=l>=100?"danger":l>=80?"warning":"ok",y=d>=100?"danger":d>=80?"warning":"ok";return o`
import{a as f}from"./chunk-SHU63LHK.js";import{a as h,b as o,d as b,e as m,f as g,g as n,h as _,j as a,l as v,s as p}from"./chunk-GKOWV2CF.js";import{a as i}from"./chunk-LO2NM3CE.js";var s=class extends m{constructor(){super(...arguments);this._config={type:""};this._status=null;this._busy=!1;this._error="";this._localMonthly="";this._localYearly="";this._dirty=!1;this._loaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return this.hass?.language||"en"}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._loaded&&(this._loaded=!0,this._load(),v(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/budget_status"});this._status=t,this._localMonthly=t.monthly_budget?String(t.monthly_budget):"",this._localYearly=t.yearly_budget?String(t.yearly_budget):"",this._dirty=!1}catch(t){this._error=p(t,this._lang)}}async _save(){if(this._isAdmin){this._busy=!0,this._error="";try{let t=parseFloat(this._localMonthly),e=parseFloat(this._localYearly),r={};!isNaN(t)&&t>=0&&(r.budget_monthly=t),!isNaN(e)&&e>=0&&(r.budget_yearly=e),await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/global/update",settings:r}),await this._load()}catch(t){this._error=p(t,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_budget"),window.dispatchEvent(new CustomEvent("location-changed"))}render(){let t=this._lang,e=this._status;if(!e)return o`<ha-card><div class="loading">${a("loading",t)||"Loading\u2026"}</div></ha-card>`;let r=e.currency_symbol||_,l=e.monthly_budget?Math.min(100,(e.monthly_spent||0)/e.monthly_budget*100):0,d=e.yearly_budget?Math.min(100,(e.yearly_spent||0)/e.yearly_budget*100):0,u=l>=100?"danger":l>=80?"warning":"ok",y=d>=100?"danger":d>=80?"warning":"ok";return o`
<ha-card>
<div class="card-content">
<div class="header">
@@ -1,8 +1,8 @@
var D=globalThis,L=D.ShadowRoot&&(D.ShadyCSS===void 0||D.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,F=Symbol(),re=new WeakMap,$=class{constructor(e,t,o){if(this._$cssResult$=!0,o!==F)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(L&&e===void 0){let o=t!==void 0&&t.length===1;o&&(e=re.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),o&&re.set(t,e))}return e}toString(){return this.cssText}},ne=r=>new $(typeof r=="string"?r:r+"",void 0,F),I=(r,...e)=>{let t=r.length===1?r[0]:e.reduce((o,n,i)=>o+(a=>{if(a._$cssResult$===!0)return a.cssText;if(typeof a=="number")return a;throw Error("Value passed to 'css' function must be a 'css' function result: "+a+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(n)+r[i+1],r[0]);return new $(t,r,F)},ie=(r,e)=>{if(L)r.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(let t of e){let o=document.createElement("style"),n=D.litNonce;n!==void 0&&o.setAttribute("nonce",n),o.textContent=t.cssText,r.appendChild(o)}},B=L?r=>r:r=>r instanceof CSSStyleSheet?(e=>{let t="";for(let o of e.cssRules)t+=o.cssText;return ne(t)})(r):r;var{is:Ee,defineProperty:Ce,getOwnPropertyDescriptor:je,getOwnPropertyNames:Te,getOwnPropertySymbols:Ne,getPrototypeOf:Re}=Object,M=globalThis,ae=M.trustedTypes,ze=ae?ae.emptyScript:"",Pe=M.reactiveElementPolyfillSupport,E=(r,e)=>r,C={toAttribute(r,e){switch(e){case Boolean:r=r?ze:null;break;case Object:case Array:r=r==null?r:JSON.stringify(r)}return r},fromAttribute(r,e){let t=r;switch(e){case Boolean:t=r!==null;break;case Number:t=r===null?null:Number(r);break;case Object:case Array:try{t=JSON.parse(r)}catch{t=null}}return t}},U=(r,e)=>!Ee(r,e),se={attribute:!0,type:String,converter:C,reflect:!1,useDefault:!1,hasChanged:U};Symbol.metadata??=Symbol("metadata"),M.litPropertyMetadata??=new WeakMap;var g=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=se){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(),n=this.getPropertyDescriptor(e,o,t);n!==void 0&&Ce(this.prototype,e,n)}}static getPropertyDescriptor(e,t,o){let{get:n,set:i}=je(this.prototype,e)??{get(){return this[t]},set(a){this[t]=a}};return{get:n,set(a){let l=n?.call(this);i?.call(this,a),this.requestUpdate(e,l,o)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??se}static _$Ei(){if(this.hasOwnProperty(E("elementProperties")))return;let e=Re(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(E("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(E("properties"))){let t=this.properties,o=[...Te(t),...Ne(t)];for(let n of o)this.createProperty(n,t[n])}let e=this[Symbol.metadata];if(e!==null){let t=litPropertyMetadata.get(e);if(t!==void 0)for(let[o,n]of t)this.elementProperties.set(o,n)}this._$Eh=new Map;for(let[t,o]of this.elementProperties){let n=this._$Eu(t,o);n!==void 0&&this._$Eh.set(n,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 n of o)t.unshift(B(n))}else e!==void 0&&t.push(B(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 ie(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),n=this.constructor._$Eu(e,o);if(n!==void 0&&o.reflect===!0){let i=(o.converter?.toAttribute!==void 0?o.converter:C).toAttribute(t,o.type);this._$Em=e,i==null?this.removeAttribute(n):this.setAttribute(n,i),thLine truncated
\f\r]`,j=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,de=/-->/g,pe=/>/g,y=RegExp(`>|${W}(?:([^\\s"'>=/]+)(${W}*=${W}*(?:[^
\f\r"'\`<>=]|("|')|))|$)`,"g"),ue=/'/g,_e=/"/g,fe=/^(?:script|style|textarea|title)$/i,X=r=>(e,...t)=>({_$litType$:r,strings:e,values:t}),Ze=X(1),Xe=X(2),et=X(3),x=Symbol.for("lit-noChange"),p=Symbol.for("lit-nothing"),he=new WeakMap,b=v.createTreeWalker(v,129);function ye(r,e){if(!Z(r)||!r.hasOwnProperty("raw"))throw Error("invalid template strings array");return ce!==void 0?ce.createHTML(e):e}var Le=(r,e)=>{let t=r.length-1,o=[],n,i=e===2?"<svg>":e===3?"<math>":"",a=j;for(let l=0;l<t;l++){let s=r[l],d,u,c=-1,h=0;for(;h<s.length&&(a.lastIndex=h,u=a.exec(s),u!==null);)h=a.lastIndex,a===j?u[1]==="!--"?a=de:u[1]!==void 0?a=pe:u[2]!==void 0?(fe.test(u[2])&&(n=RegExp("</"+u[2],"g")),a=y):u[3]!==void 0&&(a=y):a===y?u[0]===">"?(a=n??j,c=-1):u[1]===void 0?c=-2:(c=a.lastIndex-u[2].length,d=u[1],a=u[3]===void 0?y:u[3]==='"'?_e:ue):a===_e||a===ue?a=y:a===de||a===pe?a=j:(a=y,n=void 0);let m=a===y&&r[l+1].startsWith("/>")?" ":"";i+=a===j?s+Oe:c>=0?(o.push(d),s.slice(0,c)+ge+s.slice(c)+f+m):s+f+(c===-2?l:m)}return[ye(r,i+(r[t]||"<?>")+(e===2?"</svg>":e===3?"</math>":"")),o]},R=class r{constructor({strings:e,_$litType$:t},o){let n;this.parts=[];let i=0,a=0,l=e.length-1,s=this.parts,[d,u]=Le(e,t);if(this.el=r.createElement(d,o),b.currentNode=this.el.content,t===2||t===3){let c=this.el.content.firstChild;c.replaceWith(...c.childNodes)}for(;(n=b.nextNode())!==null&&s.length<l;){if(n.nodeType===1){if(n.hasAttributes())for(let c of n.getAttributeNames())if(c.endsWith(ge)){let h=u[a++],m=n.getAttribute(c).split(f),O=/([.?@])?(.*)/.exec(h);s.push({type:1,index:i,name:O[2],strings:m,ctor:O[1]==="."?V:O[1]==="?"?Q:O[1]==="@"?K:S}),n.removeAttribute(c)}else c.startsWith(f)&&(s.push({type:6,index:i}),n.removeAttribute(c));if(fe.test(n.tagName)){let c=n.textContent.split(f),h=c.length-1;if(h>0){n.textContent=q?q.emptyScript:"";for(let m=0;m<h;m++)n.append(c[m],T()),b.nextNode(),s.push({type:2,index:++i});n.append(c[h],T())}}}else if(n.nodeType===8)if(n.data===me)s.push({type:2,index:i});else{let c=-1;for(;(c=n.data.indexOf(f,c+1))!==-1;)s.push({type:7,index:i}),c+=f.length-1}i++}}static createElement(e,t){let o=v.createElement("template");return o.innerHTML=e,o}};function k(r,e,t=r,o){if(e===x)return e;let n=o!==void 0?t._$Co?.[o]:t._$Cl,i=N(e)?void 0:e._$litDirective$;return n?.constructor!==i&&(n?._$AO?.(!1),i===void 0?n=void 0:(n=new i(r),n._$AT(r,t,o)),o!==void 0?(t._$Co??=[])[o]=n:t._$Cl=n),n!==void 0&&(e=k(r,n._$AS(r,e.values),n,o)),e}var G=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,n=(e?.creationScope??v).importNode(t,!0);b.currentNode=n;let i=b.nextNode(),a=0,l=0,s=o[0];for(;s!==void 0;){if(a===s.index){let d;s.type===2?d=new z(i,i.nextSibling,this,e):s.type===1?d=new s.ctor(i,s.name,s.strings,this,e):s.type===6&&(d=new J(i,this,e)),this._$AV.push(d),s=o[++l]}a!==s?.index&&(i=b.nextNode(),a++)}return b.currentNode=v,n}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++}},z=class r{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,o,n){this.type=2,this._$AH=p,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=o,this.options=n,this._$Cv=n?.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=k(this,e,t),N(e)?e===p||e==null||e===""?(this._$AH!==p&&this._$AR(),this._$AH=p):e!==this._$AH&&e!==x&&this._(e):e._$litType$!==void 0?this.$(e):e.nodeType!==void 0?this.T(e):De(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!==p&&N(this._$AH)?this._$AA.nextSibling.data=e:this.T(v.createTextNode(e)),this._$AH=e}$(e){let{values:t,_$litType$:o}=e,n=typeof o=="number"?this._$AC(e):(o.el===void 0&&(o.el=R.createElement(ye(o.h,o.h[0]),this.options)),o);if(this._$AH?._$AD===n)this._$AH.p(t);else{let i=new G(n,this),a=i.u(this.options);i.p(t),this.T(a),this._$AH=i}}_$AC(e){let t=he.get(e.strings);return t===void 0&&he.set(e.strings,t=new R(e)),t}k(e){Z(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,o,n=0;for(let i of e)n===t.length?t.push(o=new r(this.O(T()),this.O(T()),this,this.options)):o=t[n],o._$AI(i),n++;n<t.length&&(this._$AR(o&&o._$AB.nextSibling,n),t.length=n)}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(!1,!0,t);e!==this._$AB;){let o=le(e).nextSibling;le(e).remove(),e=o}}setConnected(e){this._$AM===void 0&&(this._$Cv=e,this._$AP?.(e))}},S=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,o,n,i){this.type=1,this._$AH=p,this._$AN=void 0,this.element=e,this.name=t,this._$AM=n,this.options=i,o.length>2||o[0]!==""||o[1]!==""?(this._$AH=Array(o.length-1).fill(new String),this.strings=o):this._$AH=Line truncated
var D=globalThis,L=D.ShadowRoot&&(D.ShadyCSS===void 0||D.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,F=Symbol(),re=new WeakMap,$=class{constructor(e,t,o){if(this._$cssResult$=!0,o!==F)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(L&&e===void 0){let o=t!==void 0&&t.length===1;o&&(e=re.get(t)),e===void 0&&((this.o=e=new CSSStyleSheet).replaceSync(this.cssText),o&&re.set(t,e))}return e}toString(){return this.cssText}},ne=r=>new $(typeof r=="string"?r:r+"",void 0,F),I=(r,...e)=>{let t=r.length===1?r[0]:e.reduce((o,n,i)=>o+(a=>{if(a._$cssResult$===!0)return a.cssText;if(typeof a=="number")return a;throw Error("Value passed to 'css' function must be a 'css' function result: "+a+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(n)+r[i+1],r[0]);return new $(t,r,F)},ie=(r,e)=>{if(L)r.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(let t of e){let o=document.createElement("style"),n=D.litNonce;n!==void 0&&o.setAttribute("nonce",n),o.textContent=t.cssText,r.appendChild(o)}},B=L?r=>r:r=>r instanceof CSSStyleSheet?(e=>{let t="";for(let o of e.cssRules)t+=o.cssText;return ne(t)})(r):r;var{is:Ee,defineProperty:Ce,getOwnPropertyDescriptor:je,getOwnPropertyNames:Ne,getOwnPropertySymbols:Te,getPrototypeOf:Re}=Object,M=globalThis,ae=M.trustedTypes,ze=ae?ae.emptyScript:"",Pe=M.reactiveElementPolyfillSupport,E=(r,e)=>r,C={toAttribute(r,e){switch(e){case Boolean:r=r?ze:null;break;case Object:case Array:r=r==null?r:JSON.stringify(r)}return r},fromAttribute(r,e){let t=r;switch(e){case Boolean:t=r!==null;break;case Number:t=r===null?null:Number(r);break;case Object:case Array:try{t=JSON.parse(r)}catch{t=null}}return t}},U=(r,e)=>!Ee(r,e),se={attribute:!0,type:String,converter:C,reflect:!1,useDefault:!1,hasChanged:U};Symbol.metadata??=Symbol("metadata"),M.litPropertyMetadata??=new WeakMap;var g=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=se){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(),n=this.getPropertyDescriptor(e,o,t);n!==void 0&&Ce(this.prototype,e,n)}}static getPropertyDescriptor(e,t,o){let{get:n,set:i}=je(this.prototype,e)??{get(){return this[t]},set(a){this[t]=a}};return{get:n,set(a){let l=n?.call(this);i?.call(this,a),this.requestUpdate(e,l,o)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)??se}static _$Ei(){if(this.hasOwnProperty(E("elementProperties")))return;let e=Re(this);e.finalize(),e.l!==void 0&&(this.l=[...e.l]),this.elementProperties=new Map(e.elementProperties)}static finalize(){if(this.hasOwnProperty(E("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(E("properties"))){let t=this.properties,o=[...Ne(t),...Te(t)];for(let n of o)this.createProperty(n,t[n])}let e=this[Symbol.metadata];if(e!==null){let t=litPropertyMetadata.get(e);if(t!==void 0)for(let[o,n]of t)this.elementProperties.set(o,n)}this._$Eh=new Map;for(let[t,o]of this.elementProperties){let n=this._$Eu(t,o);n!==void 0&&this._$Eh.set(n,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 n of o)t.unshift(B(n))}else e!==void 0&&t.push(B(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 ie(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),n=this.constructor._$Eu(e,o);if(n!==void 0&&o.reflect===!0){let i=(o.converter?.toAttribute!==void 0?o.converter:C).toAttribute(t,o.type);this._$Em=e,i==null?this.removeAttribute(n):this.setAttribute(n,i),thLine truncated
\f\r]`,j=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,de=/-->/g,pe=/>/g,b=RegExp(`>|${W}(?:([^\\s"'>=/]+)(${W}*=${W}*(?:[^
\f\r"'\`<>=]|("|')|))|$)`,"g"),ue=/'/g,_e=/"/g,fe=/^(?:script|style|textarea|title)$/i,X=r=>(e,...t)=>({_$litType$:r,strings:e,values:t}),Ze=X(1),Xe=X(2),et=X(3),x=Symbol.for("lit-noChange"),p=Symbol.for("lit-nothing"),he=new WeakMap,y=v.createTreeWalker(v,129);function be(r,e){if(!Z(r)||!r.hasOwnProperty("raw"))throw Error("invalid template strings array");return ce!==void 0?ce.createHTML(e):e}var Le=(r,e)=>{let t=r.length-1,o=[],n,i=e===2?"<svg>":e===3?"<math>":"",a=j;for(let l=0;l<t;l++){let s=r[l],d,u,c=-1,h=0;for(;h<s.length&&(a.lastIndex=h,u=a.exec(s),u!==null);)h=a.lastIndex,a===j?u[1]==="!--"?a=de:u[1]!==void 0?a=pe:u[2]!==void 0?(fe.test(u[2])&&(n=RegExp("</"+u[2],"g")),a=b):u[3]!==void 0&&(a=b):a===b?u[0]===">"?(a=n??j,c=-1):u[1]===void 0?c=-2:(c=a.lastIndex-u[2].length,d=u[1],a=u[3]===void 0?b:u[3]==='"'?_e:ue):a===_e||a===ue?a=b:a===de||a===pe?a=j:(a=b,n=void 0);let m=a===b&&r[l+1].startsWith("/>")?" ":"";i+=a===j?s+Oe:c>=0?(o.push(d),s.slice(0,c)+ge+s.slice(c)+f+m):s+f+(c===-2?l:m)}return[be(r,i+(r[t]||"<?>")+(e===2?"</svg>":e===3?"</math>":"")),o]},R=class r{constructor({strings:e,_$litType$:t},o){let n;this.parts=[];let i=0,a=0,l=e.length-1,s=this.parts,[d,u]=Le(e,t);if(this.el=r.createElement(d,o),y.currentNode=this.el.content,t===2||t===3){let c=this.el.content.firstChild;c.replaceWith(...c.childNodes)}for(;(n=y.nextNode())!==null&&s.length<l;){if(n.nodeType===1){if(n.hasAttributes())for(let c of n.getAttributeNames())if(c.endsWith(ge)){let h=u[a++],m=n.getAttribute(c).split(f),O=/([.?@])?(.*)/.exec(h);s.push({type:1,index:i,name:O[2],strings:m,ctor:O[1]==="."?V:O[1]==="?"?Q:O[1]==="@"?K:S}),n.removeAttribute(c)}else c.startsWith(f)&&(s.push({type:6,index:i}),n.removeAttribute(c));if(fe.test(n.tagName)){let c=n.textContent.split(f),h=c.length-1;if(h>0){n.textContent=q?q.emptyScript:"";for(let m=0;m<h;m++)n.append(c[m],N()),y.nextNode(),s.push({type:2,index:++i});n.append(c[h],N())}}}else if(n.nodeType===8)if(n.data===me)s.push({type:2,index:i});else{let c=-1;for(;(c=n.data.indexOf(f,c+1))!==-1;)s.push({type:7,index:i}),c+=f.length-1}i++}}static createElement(e,t){let o=v.createElement("template");return o.innerHTML=e,o}};function k(r,e,t=r,o){if(e===x)return e;let n=o!==void 0?t._$Co?.[o]:t._$Cl,i=T(e)?void 0:e._$litDirective$;return n?.constructor!==i&&(n?._$AO?.(!1),i===void 0?n=void 0:(n=new i(r),n._$AT(r,t,o)),o!==void 0?(t._$Co??=[])[o]=n:t._$Cl=n),n!==void 0&&(e=k(r,n._$AS(r,e.values),n,o)),e}var G=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,n=(e?.creationScope??v).importNode(t,!0);y.currentNode=n;let i=y.nextNode(),a=0,l=0,s=o[0];for(;s!==void 0;){if(a===s.index){let d;s.type===2?d=new z(i,i.nextSibling,this,e):s.type===1?d=new s.ctor(i,s.name,s.strings,this,e):s.type===6&&(d=new J(i,this,e)),this._$AV.push(d),s=o[++l]}a!==s?.index&&(i=y.nextNode(),a++)}return y.currentNode=v,n}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++}},z=class r{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(e,t,o,n){this.type=2,this._$AH=p,this._$AN=void 0,this._$AA=e,this._$AB=t,this._$AM=o,this.options=n,this._$Cv=n?.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=k(this,e,t),T(e)?e===p||e==null||e===""?(this._$AH!==p&&this._$AR(),this._$AH=p):e!==this._$AH&&e!==x&&this._(e):e._$litType$!==void 0?this.$(e):e.nodeType!==void 0?this.T(e):De(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!==p&&T(this._$AH)?this._$AA.nextSibling.data=e:this.T(v.createTextNode(e)),this._$AH=e}$(e){let{values:t,_$litType$:o}=e,n=typeof o=="number"?this._$AC(e):(o.el===void 0&&(o.el=R.createElement(be(o.h,o.h[0]),this.options)),o);if(this._$AH?._$AD===n)this._$AH.p(t);else{let i=new G(n,this),a=i.u(this.options);i.p(t),this.T(a),this._$AH=i}}_$AC(e){let t=he.get(e.strings);return t===void 0&&he.set(e.strings,t=new R(e)),t}k(e){Z(this._$AH)||(this._$AH=[],this._$AR());let t=this._$AH,o,n=0;for(let i of e)n===t.length?t.push(o=new r(this.O(N()),this.O(N()),this,this.options)):o=t[n],o._$AI(i),n++;n<t.length&&(this._$AR(o&&o._$AB.nextSibling,n),t.length=n)}_$AR(e=this._$AA.nextSibling,t){for(this._$AP?.(!1,!0,t);e!==this._$AB;){let o=le(e).nextSibling;le(e).remove(),e=o}}setConnected(e){this._$AM===void 0&&(this._$Cv=e,this._$AP?.(e))}},S=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(e,t,o,n,i){this.type=1,this._$AH=p,this._$AN=void 0,this.element=e,this.name=t,this._$AM=n,this.options=i,o.length>2||o[0]!==""||o[1]!==""?(this._$AH=Array(o.length-1).fill(new String),this.strings=o):this._$AH=Line truncated
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:"Sel Line truncated
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:"Sel Line truncated
:host {
--maint-ok-color: var(--success-color, #4caf50);
--maint-due-soon-color: var(--warning-color, #ff9800);
@@ -477,11 +477,14 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
transition: width 0.3s;
}
/* Trigger progress bar (overview rows) */
/* Trigger progress bar (overview rows). width:100% — the due-cell doesn't
stretch its children (align-items: flex-end), so without it the bar
shrinks to its label and reads shorter than the days-bar in other rows. */
.trigger-progress {
display: flex;
flex-direction: column;
gap: 2px;
width: 100%;
min-width: 90px;
}
@@ -1,4 +1,4 @@
import{a as r}from"./chunk-2LLDFX26.js";var a=r`
import{a as r}from"./chunk-GKOWV2CF.js";var a=r`
ha-card { overflow: hidden; }
.card-content {
padding: 16px;
@@ -1,4 +1,4 @@
import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l as J,m as U,n as X,o as Q,p as tt,q as et,r as it,s as k}from"./chunk-2LLDFX26.js";import{a}from"./chunk-LO2NM3CE.js";var E=class extends ${constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1}_onInput(t){let e=t.target.value;this.value=e,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:e}}))}render(){return r`
import{a as $,b as r,c as q,d as c,e as x,f as g,g as l,i as V,j as s,k as Z,l as J,m as N,n as X,o as Q,p as tt,q as et,r as it,s as k}from"./chunk-GKOWV2CF.js";import{a}from"./chunk-LO2NM3CE.js";var E=class extends x{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1}_onInput(t){let e=t.target.value;this.value=e,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:e}}))}render(){return r`
<label class="field">
${this.label?r`<span class="label">${this.label}${this.required?r`<span class="req">*</span>`:c}</span>`:c}
<input
@@ -16,7 +16,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
/>
${this.helper?r`<span class="helper">${this.helper}</span>`:c}
</label>
`}};E.styles=x`
`}};E.styles=$`
:host { display: block; }
.field {
display: flex;
@@ -50,7 +50,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
color: var(--secondary-text-color);
font-style: italic;
}
`,a([g()],E.prototype,"label",2),a([g()],E.prototype,"value",2),a([g()],E.prototype,"placeholder",2),a([g()],E.prototype,"type",2),a([g({type:Boolean})],E.prototype,"required",2),a([g({type:Boolean})],E.prototype,"disabled",2),a([g()],E.prototype,"step",2),a([g()],E.prototype,"min",2),a([g()],E.prototype,"max",2),a([g()],E.prototype,"pattern",2),a([g()],E.prototype,"helper",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",E);var f=class extends ${constructor(){super(...arguments);this.objects=[];this._open=!1;this._loading=!1;this._error="";this._name="";this._manufacturer="";this._model="";this._serialNumber="";this._areaId="";this._installationDate="";this._warrantyExpiry="";this._documentationUrl="";this._notes="";this._haDeviceId="";this._parentEntryId="";this._entryId=null}get _lang(){return this.hass?.language??navigator.language.split("-")[0]??"en"}openCreate(){this._entryId=null,this._name="",this._manufacturer="",this._model="",this._serialNumber="",this._areaId="",this._installationDate="",this._warrantyExpiry="",this._documentationUrl="",this._notes="",this._haDeviceId="",this._parentEntryId="",this._error="",this._open=!0}openEdit(t,e){this._entryId=t,this._name=e.name||"",this._manufacturer=e.manufacturer||"",this._model=e.model||"",this._serialNumber=e.serial_number||"",this._areaId=e.area_id||"",this._installationDate=e.installation_date||"",this._warrantyExpiry=e.warranty_expiry||"",this._documentationUrl=e.documentation_url||"",this._notes=e.notes||"",this._haDeviceId=e.ha_device_id||"",this._parentEntryId=e.parent_entry_id||"",this._error="",this._open=!0}async _save(){if(!this._loading&&this._name.trim()){this._loading=!0,this._error="";try{this._entryId?await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/update",entry_id:this._entryId,name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}):await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/create",name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}),this._open=!1,this.dispatchEvent(new CustomEvent("object-saved"))}catch(t){this._error=k(t,this._lang,s("save_error",this._lang))}finally{this._loading=!1}}}_parentChoices(){return(this.objects||[]).filter(t=>t.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return r``;let t=this._lang,e=this._entryId?s("edit_object",t):s("new_object",t);return r`
`,a([g()],E.prototype,"label",2),a([g()],E.prototype,"value",2),a([g()],E.prototype,"placeholder",2),a([g()],E.prototype,"type",2),a([g({type:Boolean})],E.prototype,"required",2),a([g({type:Boolean})],E.prototype,"disabled",2),a([g()],E.prototype,"step",2),a([g()],E.prototype,"min",2),a([g()],E.prototype,"max",2),a([g()],E.prototype,"pattern",2),a([g()],E.prototype,"helper",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",E);var f=class extends x{constructor(){super(...arguments);this.objects=[];this._open=!1;this._loading=!1;this._error="";this._name="";this._manufacturer="";this._model="";this._serialNumber="";this._areaId="";this._installationDate="";this._warrantyExpiry="";this._documentationUrl="";this._notes="";this._haDeviceId="";this._parentEntryId="";this._entryId=null}get _lang(){return this.hass?.language??navigator.language.split("-")[0]??"en"}openCreate(){this._entryId=null,this._name="",this._manufacturer="",this._model="",this._serialNumber="",this._areaId="",this._installationDate="",this._warrantyExpiry="",this._documentationUrl="",this._notes="",this._haDeviceId="",this._parentEntryId="",this._error="",this._open=!0}openEdit(t,e){this._entryId=t,this._name=e.name||"",this._manufacturer=e.manufacturer||"",this._model=e.model||"",this._serialNumber=e.serial_number||"",this._areaId=e.area_id||"",this._installationDate=e.installation_date||"",this._warrantyExpiry=e.warranty_expiry||"",this._documentationUrl=e.documentation_url||"",this._notes=e.notes||"",this._haDeviceId=e.ha_device_id||"",this._parentEntryId=e.parent_entry_id||"",this._error="",this._open=!0}async _save(){if(!this._loading&&this._name.trim()){this._loading=!0,this._error="";try{this._entryId?await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/update",entry_id:this._entryId,name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}):await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/create",name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}),this._open=!1,this.dispatchEvent(new CustomEvent("object-saved"))}catch(t){this._error=k(t,this._lang,s("save_error",this._lang))}finally{this._loading=!1}}}_parentChoices(){return(this.objects||[]).filter(t=>t.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return r``;let t=this._lang,e=this._entryId?s("edit_object",t):s("new_object",t);return r`
<ha-dialog open @closed=${this._close}>
<div class="dialog-title">${e}</div>
<div class="content">
@@ -144,7 +144,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
</ha-button>
</div>
</ha-dialog>
`}};f.styles=x`
`}};f.styles=$`
.dialog-title {
font-size: 18px;
font-weight: 500;
@@ -191,15 +191,15 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
color: var(--error-color, #f44336);
font-size: 13px;
}
`,a([g({attribute:!1})],f.prototype,"hass",2),a([g({attribute:!1})],f.prototype,"objects",2),a([l()],f.prototype,"_open",2),a([l()],f.prototype,"_loading",2),a([l()],f.prototype,"_error",2),a([l()],f.prototype,"_name",2),a([l()],f.prototype,"_manufacturer",2),a([l()],f.prototype,"_model",2),a([l()],f.prototype,"_serialNumber",2),a([l()],f.prototype,"_areaId",2),a([l()],f.prototype,"_installationDate",2),a([l()],f.prototype,"_warrantyExpiry",2),a([l()],f.prototype,"_documentationUrl",2),a([l()],f.prototype,"_notes",2),a([l()],f.prototype,"_haDeviceId",2),a([l()],f.prototype,"_parentEntryId",2),a([l()],f.prototype,"_entryId",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",f);var W=class{constructor(d){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=d}updateHass(d){this.hass=d}async getUsers(d=!1){let t=Date.now();if(!d&&this.usersCache&&t-this.cacheTimestamp<this.CACHE_TTL_MS)return this.usersCache;try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/users/list"});return this.usersCache=e.users,this.cacheTimestamp=t,this.usersCache}catch(e){return console.error("Failed to fetch users:",e),this.usersCache||[]}}async assignUser(d,t,e){await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/assign_user",entry_id:d,task_id:t,user_id:e})}async getTasksByUser(d){return(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tasks/by_user",user_id:d})).tasks}getUserName(d){return!d||!this.usersCache?null:this.usersCache.find(e=>e.id===d)?.name||null}getUser(d){return!d||!this.usersCache?null:this.usersCache.find(t=>t.id===d)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(d){return d?d===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}};var yt=["cleaning","inspection","replacement","calibration","service","reading","custom"],xt=["low","normal","high"],$t=["time_based","weekdays","nth_weekday","day_of_month","sensor_based","one_time","manual"],kt=["weekdays","nth_weekday","day_of_month"],st=["threshold","counter","state_change","runtime"],wt=[...st,"compound"];function Et(){return{entityIds:"",type:"threshold",above:"",below:"",forMinutes:"0",targetValue:"",deltaMode:!1,fromState:"",toState:"",targetChanges:"",runtimeHours:""}}function It(n){return{entityIds:(n.entity_ids||(n.entity_id?[n.entity_id]:[])).join(", "),type:n.type||"threshold",above:n.trigger_above?.toString()??"",below:n.trigger_below?.toString()??"",forMinutes:n.trigger_for_minutes?.toString()??"0",targetValue:n.trigger_target_value?.toString()??"",deltaMode:n.trigger_delta_mode||!1,fromState:n.trigger_from_state||"",toState:n.trigger_to_state||"",targetChanges:n.trigger_target_changes?.toString()??"",runtimeHours:n.trigger_runtime_hours?.toString()??""}}function Tt(n){let d=n.entityIds.split(",").map(e=>e.trim()).filter(Boolean);if(d.length===0)return null;let t={entity_id:d[0],entity_ids:d,type:n.type};if(n.type==="threshold"){let e=parseFloat(n.above);isNaN(e)||(t.trigger_above=e);let i=parseFloat(n.below);isNaN(i)||(t.trigger_below=i);let o=parseInt(n.forMinutes,10);isNaN(o)||(t.trigger_for_minutes=o)}else if(n.type==="counter"){let e=parseFloat(n.targetValue);isNaN(e)||(t.trigger_target_value=e),t.trigger_delta_mode=n.deltaMode}else if(n.type==="state_change"){n.fromState&&(t.trigger_from_state=n.fromState),n.toState&&(t.trigger_to_state=n.toState);let e=parseInt(n.targetChanges,10);isNaN(e)||(t.trigger_target_changes=e)}else if(n.type==="runtime"){let e=parseFloat(n.runtimeHours);isNaN(e)||(t.trigger_runtime_hours=e)}return t}function At(n){return Array.from({length:7},(d,t)=>Q(t,n,"short"))}var h=class extends ${constructor(){super(...arguments);this.checklistsEnabled=!1;this.scheduleTimeEnabled=!1;this.completionActionsEnabled=!1;this.defaultWarningDays=7;this._open=!1;this._loading=!1;this._error="";this._entryId="";this._taskId=null;this._objectChoices=[];this._name="";this._type="custom";this._scheduleType="time_based";this._intervalDays="30";this._intervalUnit="days";this._dueDate="";this._warningDays="7";this._earliestCompletionDays="";this._intervalAnchor="completion";this._weekdays=[];this._nth="1";this._nthWeekday="5";this._domDay="1";this._domLastDay=!1;this._domBusiness=!1;this._calOffset="0";this._notes="";this._documentationUrl="";this._customIcon="";this._priority="normal";this._labels="";this._enabled=!0;this._triggerEntityId="";this._triggerEntityIds=[];this._triggerEntityLogic="any";this._triggerAttribute="";this._triggerType="threshold";this._triggerAbove="";this._triggerBelow="";this._triggerForMinutes="0";this._triggerTargetValue="";this._triggerDeltaMode=!1;this._autoCompleteOnRecovery=!1;this._triggerFromState="";this._triggerToState="";this._triggerTargetChanges="";this._triggerRuntimeHours="";this._compoundLogic="AND";this._compoundConditions=[];this._suggestedAttributes=[];this._availableAttributes=[];this._Line truncated
`),this._scheduleTime=e.schedule_time||"";let o=e.on_complete_action;if(o&&o.service){this._actionService=o.service;let p=o.target?.entity_id;this._actionTargetEntity=Array.isArray(p)?p[0]||"":p||"",this._actionData=o.data&&typeof o.data=="object"?{...o.data}:{},this._actionDataJsonFallback=""}else this._actionService="",this._actionTargetEntity="",this._actionData={},this._actionDataJsonFallback="";let u=e.quick_complete_defaults;this._qcNotes=u?.notes||"",this._qcCost=u?.cost!=null?String(u.cost):"",this._qcDuration=u?.duration!=null?String(u.duration):"",this._qcFeedback=u?.feedback||"";let _=e.adaptive_config||{};if(this._environmentalEntity=_.environmental_entity||"",this._environmentalAttribute=_.environmental_attribute||"",this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute,e.trigger_config){let p=e.trigger_config;this._triggerEntityId=p.entity_id||"",this._triggerEntityIds=p.entity_ids||(p.entity_id?[p.entity_id]:[]),this._triggerEntityLogic=p.entity_logic||"any",this._triggerAttribute=p.attribute||"",this._triggerType=p.type||"threshold",this._triggerAbove=p.trigger_above?.toString()||"",this._triggerBelow=p.trigger_below?.toString()||"",this._triggerForMinutes=p.trigger_for_minutes?.toString()||"0",this._triggerTargetValue=p.trigger_target_value?.toString()||"",this._triggerDeltaMode=p.trigger_delta_mode||!1,this._autoCompleteOnRecovery=p.auto_complete_on_recovery||!1,this._triggerFromState=p.trigger_from_state||"",this._triggerToState=p.trigger_to_state||"",this._triggerTargetChanges=p.trigger_target_changes?.toString()||"",this._triggerRuntimeHours=p.trigger_runtime_hours?.toString()||"",p.type==="compound"?(this._compoundLogic=p.compound_logic==="OR"?"OR":"AND",this._compoundConditions=(p.conditions||[]).map(It)):(this._compoundLogic="AND",this._compoundConditions=[])}else this._resetTriggerFields();this._triggerEntityId&&this._fetchEntityAttributes(this._triggerEntityId),await Promise.all([this._loadUsers(),this._loadTags()]),this._open=!0}_resetFields(){this._name="",this._type="custom",this._scheduleType="time_based",this._intervalDays="30",this._intervalUnit="days",this._dueDate="",this._warningDays=String(this.defaultWarningDays),this._earliestCompletionDays="",this._intervalAnchor="completion",this._weekdays=[],this._nth="1",this._nthWeekday="5",this._domDay="1",this._domLastDay=!1,this._domBusiness=!1,this._calOffset="0",this._notes="",this._documentationUrl="",this._customIcon="",this._priority="normal",this._labels="",this._enabled=!0,this._lastPerformed="",this._nfcTagId="",this._readingUnit="",this._responsibleUserId=null,this._assigneePool=[],this._rotationStrategy="",this._checklistText="",this._scheduleTime="",this._environmentalEntity="",this._environmentalAttribute="",this._environmentalInitial="",this._environmentalAttributeInitial="",this._actionService="",this._actionTargetEntity="",this._actionData={},this._actionDataJsonFallback="",this._actionTesting=!1,this._actionTestResult="",this._qcNotes="",this._qcCost="",this._qcDuration="",this._qcFeedback="",this._resetTriggerFields()}_resetTriggerFields(){this._triggerEntityId="",this._triggerEntityIds=[],this._triggerEntityLogic="any",this._triggerAttribute="",this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain="",this._triggerType="threshold",this._triggerAbove="",this._triggerBelow="",this._triggerForMinutes="0",this._triggerTargetValue="",this._triggerDeltaMode=!1,this._autoCompleteOnRecovery=!1,this._triggerFromState="",this._triggerToState="",this._triggerTargetChanges="",this._triggerRuntimeHours="",this._compoundLogic="AND",this._compoundConditions=[]}async _loadUsers(){this._userService||(this._userService=new W(this.hass));try{this._availableUsers=await this._userService.getUsers()}catch(t){console.error("Failed to load users:",t),this._availableUsers=[]}}_toggleAssignee(t){this._assigneePool=this._assigneePool.includes(t)?this._assigneePool.filter(e=>e!==t):[...this._assigneePool,t]}async _testAction(){let t=this._actionService.trim();if(!t||!/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(t)){this._actionTestResult="error",this._actionTestError="Invalid service format (expected 'domain.service')",setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},5e3);return}let[e,i]=t.split(".");if(!this.hass?.services?.[e]?.[i]){this._actionTestResult="error",this._actionTestError=`Service "${t}" is not registered in Home Assistant. Check spelling and that the integration providing it is loaded.`,setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},8e3);return}let o=this._actionTargetEntity.trim();if(o){let u=o.split(".")[0];if(u!==e&&!new Set(["homeassistant","scene","notify","persistent_notification"]).has(e)){this._actionTestResult="error",this._actionTestError=`Service "${t}" only works on ${e}.* entities; entity "${o}" is in ${u}.* \u2014 pick a service that matches the entity domain (e.g. ${u}.${i})`,setTimeout(()=> Line truncated
`,a([g({attribute:!1})],f.prototype,"hass",2),a([g({attribute:!1})],f.prototype,"objects",2),a([l()],f.prototype,"_open",2),a([l()],f.prototype,"_loading",2),a([l()],f.prototype,"_error",2),a([l()],f.prototype,"_name",2),a([l()],f.prototype,"_manufacturer",2),a([l()],f.prototype,"_model",2),a([l()],f.prototype,"_serialNumber",2),a([l()],f.prototype,"_areaId",2),a([l()],f.prototype,"_installationDate",2),a([l()],f.prototype,"_warrantyExpiry",2),a([l()],f.prototype,"_documentationUrl",2),a([l()],f.prototype,"_notes",2),a([l()],f.prototype,"_haDeviceId",2),a([l()],f.prototype,"_parentEntryId",2),a([l()],f.prototype,"_entryId",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",f);var W=class{constructor(d){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=d}updateHass(d){this.hass=d}async getUsers(d=!1){let t=Date.now();if(!d&&this.usersCache&&t-this.cacheTimestamp<this.CACHE_TTL_MS)return this.usersCache;try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/users/list"});return this.usersCache=e.users,this.cacheTimestamp=t,this.usersCache}catch(e){return console.error("Failed to fetch users:",e),this.usersCache||[]}}async assignUser(d,t,e){await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/assign_user",entry_id:d,task_id:t,user_id:e})}async getTasksByUser(d){return(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tasks/by_user",user_id:d})).tasks}getUserName(d){return!d||!this.usersCache?null:this.usersCache.find(e=>e.id===d)?.name||null}getUser(d){return!d||!this.usersCache?null:this.usersCache.find(t=>t.id===d)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(d){return d?d===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}};var $t=["cleaning","inspection","replacement","calibration","service","reading","custom"],xt=["low","normal","high"],kt=["time_based","weekdays","nth_weekday","day_of_month","sensor_based","one_time","manual"],st=["weekdays","nth_weekday","day_of_month"],at=["threshold","counter","state_change","runtime"],wt=[...at,"compound"];function Et(){return{entityIds:"",type:"threshold",above:"",below:"",forMinutes:"0",targetValue:"",deltaMode:!1,fromState:"",toState:"",targetChanges:"",runtimeHours:""}}function It(n){return{entityIds:(n.entity_ids||(n.entity_id?[n.entity_id]:[])).join(", "),type:n.type||"threshold",above:n.trigger_above?.toString()??"",below:n.trigger_below?.toString()??"",forMinutes:n.trigger_for_minutes?.toString()??"0",targetValue:n.trigger_target_value?.toString()??"",deltaMode:n.trigger_delta_mode||!1,fromState:n.trigger_from_state||"",toState:n.trigger_to_state||"",targetChanges:n.trigger_target_changes?.toString()??"",runtimeHours:n.trigger_runtime_hours?.toString()??""}}function Tt(n){let d=n.entityIds.split(",").map(e=>e.trim()).filter(Boolean);if(d.length===0)return null;let t={entity_id:d[0],entity_ids:d,type:n.type};if(n.type==="threshold"){let e=parseFloat(n.above);isNaN(e)||(t.trigger_above=e);let i=parseFloat(n.below);isNaN(i)||(t.trigger_below=i);let o=parseInt(n.forMinutes,10);isNaN(o)||(t.trigger_for_minutes=o)}else if(n.type==="counter"){let e=parseFloat(n.targetValue);isNaN(e)||(t.trigger_target_value=e),t.trigger_delta_mode=n.deltaMode}else if(n.type==="state_change"){n.fromState&&(t.trigger_from_state=n.fromState),n.toState&&(t.trigger_to_state=n.toState);let e=parseInt(n.targetChanges,10);isNaN(e)||(t.trigger_target_changes=e)}else if(n.type==="runtime"){let e=parseFloat(n.runtimeHours);isNaN(e)||(t.trigger_runtime_hours=e)}return t}function At(n){return Array.from({length:7},(d,t)=>Q(t,n,"short"))}function Lt(n){let d=new Intl.DateTimeFormat(n||"en",{month:"short"});return Array.from({length:12},(t,e)=>d.format(new Date(2021,e,1)))}var p=class extends x{constructor(){super(...arguments);this.checklistsEnabled=!1;this.scheduleTimeEnabled=!1;this.completionActionsEnabled=!1;this.defaultWarningDays=7;this._open=!1;this._loading=!1;this._error="";this._entryId="";this._taskId=null;this._objectChoices=[];this._name="";this._type="custom";this._scheduleType="time_based";this._intervalDays="30";this._intervalUnit="days";this._dueDate="";this._warningDays="7";this._earliestCompletionDays="";this._intervalAnchor="completion";this._weekdays=[];this._nth="1";this._nthWeekday="5";this._domDay="1";this._domLastDay=!1;this._domBusiness=!1;this._calOffset="0";this._seasonMonths=[];this._endsMode="never";this._endsCount="";this._endsUntil="";this._notes="";this._documentationUrl="";this._customIcon="";this._priority="normal";this._labels="";this._enabled=!0;this._triggerEntityId="";this._triggerEntityIds=[];this._triggerEntityLogic="any";this._triggerAttribute="";this._triggerType="threshold";this._triggerAbove="";this._triggerBelow="";this._triggerForMinutes="0";this._triggerTargetValue="";this._triggerDeltaMode=!1;this._autoCompleteOnRecovery=!1;this._t Line truncated
`),this._scheduleTime=e.schedule_time||"";let _=e.on_complete_action;if(_&&_.service){this._actionService=_.service;let u=_.target?.entity_id;this._actionTargetEntity=Array.isArray(u)?u[0]||"":u||"",this._actionData=_.data&&typeof _.data=="object"?{..._.data}:{},this._actionDataJsonFallback=""}else this._actionService="",this._actionTargetEntity="",this._actionData={},this._actionDataJsonFallback="";let h=e.quick_complete_defaults;this._qcNotes=h?.notes||"",this._qcCost=h?.cost!=null?String(h.cost):"",this._qcDuration=h?.duration!=null?String(h.duration):"",this._qcFeedback=h?.feedback||"";let m=e.adaptive_config||{};if(this._environmentalEntity=m.environmental_entity||"",this._environmentalAttribute=m.environmental_attribute||"",this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute,e.trigger_config){let u=e.trigger_config;this._triggerEntityId=u.entity_id||"",this._triggerEntityIds=u.entity_ids||(u.entity_id?[u.entity_id]:[]),this._triggerEntityLogic=u.entity_logic||"any",this._triggerAttribute=u.attribute||"",this._triggerType=u.type||"threshold",this._triggerAbove=u.trigger_above?.toString()||"",this._triggerBelow=u.trigger_below?.toString()||"",this._triggerForMinutes=u.trigger_for_minutes?.toString()||"0",this._triggerTargetValue=u.trigger_target_value?.toString()||"",this._triggerDeltaMode=u.trigger_delta_mode||!1,this._autoCompleteOnRecovery=u.auto_complete_on_recovery||!1,this._triggerFromState=u.trigger_from_state||"",this._triggerToState=u.trigger_to_state||"",this._triggerTargetChanges=u.trigger_target_changes?.toString()||"",this._triggerRuntimeHours=u.trigger_runtime_hours?.toString()||"",u.type==="compound"?(this._compoundLogic=u.compound_logic==="OR"?"OR":"AND",this._compoundConditions=(u.conditions||[]).map(It)):(this._compoundLogic="AND",this._compoundConditions=[])}else this._resetTriggerFields();this._triggerEntityId&&this._fetchEntityAttributes(this._triggerEntityId),await Promise.all([this._loadUsers(),this._loadTags()]),this._open=!0}_resetFields(){this._name="",this._type="custom",this._scheduleType="time_based",this._intervalDays="30",this._intervalUnit="days",this._dueDate="",this._warningDays=String(this.defaultWarningDays),this._earliestCompletionDays="",this._intervalAnchor="completion",this._weekdays=[],this._nth="1",this._nthWeekday="5",this._domDay="1",this._domLastDay=!1,this._domBusiness=!1,this._calOffset="0",this._seasonMonths=[],this._endsMode="never",this._endsCount="",this._endsUntil="",this._notes="",this._documentationUrl="",this._customIcon="",this._priority="normal",this._labels="",this._enabled=!0,this._lastPerformed="",this._nfcTagId="",this._readingUnit="",this._responsibleUserId=null,this._assigneePool=[],this._rotationStrategy="",this._checklistText="",this._scheduleTime="",this._environmentalEntity="",this._environmentalAttribute="",this._environmentalInitial="",this._environmentalAttributeInitial="",this._actionService="",this._actionTargetEntity="",this._actionData={},this._actionDataJsonFallback="",this._actionTesting=!1,this._actionTestResult="",this._qcNotes="",this._qcCost="",this._qcDuration="",this._qcFeedback="",this._resetTriggerFields()}_resetTriggerFields(){this._triggerEntityId="",this._triggerEntityIds=[],this._triggerEntityLogic="any",this._triggerAttribute="",this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain="",this._triggerType="threshold",this._triggerAbove="",this._triggerBelow="",this._triggerForMinutes="0",this._triggerTargetValue="",this._triggerDeltaMode=!1,this._autoCompleteOnRecovery=!1,this._triggerFromState="",this._triggerToState="",this._triggerTargetChanges="",this._triggerRuntimeHours="",this._compoundLogic="AND",this._compoundConditions=[]}async _loadUsers(){this._userService||(this._userService=new W(this.hass));try{this._availableUsers=await this._userService.getUsers()}catch(t){console.error("Failed to load users:",t),this._availableUsers=[]}}_toggleAssignee(t){this._assigneePool=this._assigneePool.includes(t)?this._assigneePool.filter(e=>e!==t):[...this._assigneePool,t]}async _testAction(){let t=this._actionService.trim();if(!t||!/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(t)){this._actionTestResult="error",this._actionTestError="Invalid service format (expected 'domain.service')",setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},5e3);return}let[e,i]=t.split(".");if(!this.hass?.services?.[e]?.[i]){this._actionTestResult="error",this._actionTestError=`Service "${t}" is not registered in Home Assistant. Check spelling and that the integration providing it is loaded.`,setTimeout(()=>{this._actionTestResult="",this._actionTestError=""},8e3);return}let o=this._actionTargetEntity.trim();if(o){let _=o.split(".")[0];if(_!==e&&!new Set(["homeassistant","scene","notify","persistent_notification"]).has(e)){this._actionTestResult="error",this._actionTestError=`Service "${t}" only works on ${e}.* entities; entity "${o}" is in ${_}.* \u2Line truncated
<details class="ca-section">
<summary>${s("on_complete_action_title",t)}</summary>
<p class="field-help">${s("on_complete_action_desc",t)}</p>
<ha-service-picker
.hass=${this.hass}
.value=${this._actionService}
@value-changed=${i=>{this._actionService=i.detail.value||"";let o=this._serviceSchema();if(o){let u=new Set(o.map(_=>_.name));this._actionData=Object.fromEntries(Object.entries(this._actionData).filter(([_])=>u.has(_)))}}}
@value-changed=${i=>{this._actionService=i.detail.value||"";let o=this._serviceSchema();if(o){let _=new Set(o.map(h=>h.name));this._actionData=Object.fromEntries(Object.entries(this._actionData).filter(([h])=>_.has(h)))}}}
></ha-service-picker>
<ha-form
.hass=${this.hass}
@@ -268,8 +268,8 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
<option value="not_needed">${s("quick_complete_defaults_feedback_not_needed",t)}</option>
</select>
</details>
`}async _loadTags(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tags/list"});this._availableTags=t.tags||[]}catch{this._availableTags=[]}}async _fetchEntityAttributes(t){if(!t||!this.hass){this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain="";return}try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/entity/attributes",entity_id:t});this._entityDomain=e.domain||"",this._suggestedAttributes=e.suggested_attributes||[],this._availableAttributes=e.available_attributes||[]}catch{this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain=""}}async _save(){if(!this._loading&&this._name.trim()){this._loading=!0,this._error="";try{let t={type:this._taskId?"maintenance_supporter/task/update":"maintenance_supporter/task/create",entry_id:this._entryId,name:this._name,task_type:this._type,schedule_type:this._scheduleType,warning_days:parseInt(this._warningDays,10)||7},e=this._earliestCompletionDays.trim();if(t.earliest_completion_days=e===""?null:Math.max(0,parseInt(e,10)||0),this._taskId&&(t.task_id=this._taskId),this._scheduleType==="one_time"?(t.due_date=this._dueDate||null,t.interval_days=null):kt.includes(this._scheduleType)?(t.schedule=this._buildSchedule(),t.interval_days=null,this._taskId&&(t.due_date=null)):(this._taskId&&(t.due_date=null),this._scheduleType!=="manual"&&this._intervalDays?(t.interval_days=parseInt(this._intervalDays,10),t.interval_unit=this._intervalUnit,t.interval_anchor=this._intervalAnchor):this._taskId&&(t.interval_days=null,t.interval_anchor="completion")),t.notes=this._notes||null,t.documentation_url=this._documentationUrl||null,t.custom_icon=this._customIcon||null,t.priority=this._priority,t.labels=this._labels.split(",").map(_=>_.trim()).filter(Boolean),t.enabled=this._enabled,t.last_performed=this._lastPerformed||null,t.nfc_tag_id=this._nfcTagId||null,t.reading_unit=this._readingUnit.trim()||null,t.responsible_user_id=this._responsibleUserId,t.assignee_pool=this._assigneePool,t.rotation_strategy=this._assigneePool.length>=2&&this._rotationStrategy?this._rotationStrategy:null,this._scheduleType==="sensor_based"&&this._triggerType==="compound"){let _=this._compoundConditions.map(Tt).filter(p=>p!==null);if(_.length>0){let p={type:"compound",compound_logic:this._compoundLogic,conditions:_};this._autoCompleteOnRecovery&&(p.auto_complete_on_recovery=!0),t.trigger_config=p}else this._taskId&&(t.trigger_config=null)}else if(this._scheduleType==="sensor_based"&&this._triggerEntityId){let _=this._triggerEntityIds.length>0?this._triggerEntityIds:[this._triggerEntityId],p={entity_id:_[0],entity_ids:_,type:this._triggerType};if(this._triggerAttribute&&(p.attribute=this._triggerAttribute),this._autoCompleteOnRecovery&&(p.auto_complete_on_recovery=!0),_.length>1&&(p.entity_logic=this._triggerEntityLogic),this._triggerType==="threshold"){if(this._triggerAbove){let m=parseFloat(this._triggerAbove);isNaN(m)||(p.trigger_above=m)}if(this._triggerBelow){let m=parseFloat(this._triggerBelow);isNaN(m)||(p.trigger_below=m)}if(this._triggerForMinutes){let m=parseInt(this._triggerForMinutes,10);isNaN(m)||(p.trigger_for_minutes=m)}}else if(this._triggerType==="counter"){if(this._triggerTargetValue){let m=parseFloat(this._triggerTargetValue);isNaN(m)||(p.trigger_target_value=m)}p.trigger_delta_mode=this._triggerDeltaMode}else if(this._triggerType==="state_change"){if(this._triggerFromState&&(p.trigger_from_state=this._triggerFromState),this._triggerToState&&(p.trigger_to_state=this._triggerToState),this._triggerTargetChanges){let m=parseInt(this._triggerTargetChanges,10);isNaN(m)||(p.trigger_target_changes=m)}}else if(this._triggerType==="runtime"&&this._triggerRuntimeHours){let m=parseFloat(this._triggerRuntimeHours);isNaN(m)||(p.trigger_runtime_hours=m)}t.trigger_config=p}else this._taskId&&(t.trigger_config=null);if(this.scheduleTimeEnabled&&this._scheduleType==="time_based"){let _=this._scheduleTime.trim();t.schedule_time=/^([01]\d|2[0-3]):[0-5]\d$/.test(_)?_:null}if(this.checklistsEnabled){let _=this._checklistText.split(`
`).map(p=>p.trim()).filter(Boolean).slice(0,100);t.checklist=_.length?_:null}if(this.completionActionsEnabled){let _=this._actionService.trim();if(_&&/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(_)){let I={service:_},z=this._actionTargetEntity.trim();z&&(I.target={entity_id:z});let w=this._buildActionData();Object.keys(w).length>0&&(I.data=w),t.on_complete_action=I}else t.on_complete_action=null;let p={};this._qcNotes.trim()&&(p.notes=this._qcNotes.trim());let m=parseFloat(this._qcCost);!isNaN(m)&&m>=0&&(p.cost=m);let y=parseInt(this._qcDuration,10);!isNaN(y)&&y>=0&&(p.duration=y),this._qcFeedback&&(p.feedback=this._qcFeedback),t.quick_complete_defaults=Object.keys(p).length?p:null}let i=await this.hass.connection.sendMessagePromise(t),o=this._taskId||i?.task_id,u=this._environmentalEntity!==this._environmentalInitial||this._environmentalAttribute!==this._environmentalAttributeInitial;if(o&&this._scheduleType==="sensor_based"&&u)try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/set_environmental_entity",entry_id:this._entryId,task_id:o,environmental_entity:this._environmentalEntity||null,environmental_attribute:this._environmentalAttribute||null}),this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute}catch{}this._open=!1,this.dispatchEvent(new CustomEvent("task-saved"))}catch(t){this._error=k(t,this._lang,s("save_error",this._lang))}finally{this._loading=!1}}}_close(){this._open=!1}_renderTriggerFields(){if(this._scheduleType!=="sensor_based")return c;let t=this._lang,e=this._triggerType==="compound";return r`
`}async _loadTags(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tags/list"});this._availableTags=t.tags||[]}catch{this._availableTags=[]}}async _fetchEntityAttributes(t){if(!t||!this.hass){this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain="";return}try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/entity/attributes",entity_id:t});this._entityDomain=e.domain||"",this._suggestedAttributes=e.suggested_attributes||[],this._availableAttributes=e.available_attributes||[]}catch{this._suggestedAttributes=[],this._availableAttributes=[],this._entityDomain=""}}async _save(){if(!this._loading&&this._name.trim()){this._loading=!0,this._error="";try{let t={type:this._taskId?"maintenance_supporter/task/update":"maintenance_supporter/task/create",entry_id:this._entryId,name:this._name,task_type:this._type,schedule_type:this._scheduleType,warning_days:parseInt(this._warningDays,10)||7},e=this._earliestCompletionDays.trim();if(t.earliest_completion_days=e===""?null:Math.max(0,parseInt(e,10)||0),this._taskId&&(t.task_id=this._taskId),this._scheduleType==="one_time"?(t.due_date=this._dueDate||null,t.interval_days=null):st.includes(this._scheduleType)?(t.schedule={...this._buildSchedule(),...this._recurrenceExtras()},t.interval_days=null,this._taskId&&(t.due_date=null)):(this._taskId&&(t.due_date=null),this._scheduleType!=="manual"&&this._intervalDays?(t.interval_days=parseInt(this._intervalDays,10),t.interval_unit=this._intervalUnit,t.interval_anchor=this._intervalAnchor,this._scheduleType==="time_based"&&(t.schedule={kind:"interval",...this._recurrenceExtras()})):this._taskId&&(t.interval_days=null,t.interval_anchor="completion")),t.notes=this._notes||null,t.documentation_url=this._documentationUrl||null,t.custom_icon=this._customIcon||null,t.priority=this._priority,t.labels=this._labels.split(",").map(h=>h.trim()).filter(Boolean),t.enabled=this._enabled,t.last_performed=this._lastPerformed||null,t.nfc_tag_id=this._nfcTagId||null,t.reading_unit=this._readingUnit.trim()||null,t.responsible_user_id=this._responsibleUserId,t.assignee_pool=this._assigneePool,t.rotation_strategy=this._assigneePool.length>=2&&this._rotationStrategy?this._rotationStrategy:null,this._scheduleType==="sensor_based"&&this._triggerType==="compound"){let h=this._compoundConditions.map(Tt).filter(m=>m!==null);if(h.length>0){let m={type:"compound",compound_logic:this._compoundLogic,conditions:h};this._autoCompleteOnRecovery&&(m.auto_complete_on_recovery=!0),t.trigger_config=m}else this._taskId&&(t.trigger_config=null)}else if(this._scheduleType==="sensor_based"&&this._triggerEntityId){let h=this._triggerEntityIds.length>0?this._triggerEntityIds:[this._triggerEntityId],m={entity_id:h[0],entity_ids:h,type:this._triggerType};if(this._triggerAttribute&&(m.attribute=this._triggerAttribute),this._autoCompleteOnRecovery&&(m.auto_complete_on_recovery=!0),h.length>1&&(m.entity_logic=this._triggerEntityLogic),this._triggerType==="threshold"){if(this._triggerAbove){let u=parseFloat(this._triggerAbove);isNaN(u)||(m.trigger_above=u)}if(this._triggerBelow){let u=parseFloat(this._triggerBelow);isNaN(u)||(m.trigger_below=u)}if(this._triggerForMinutes){let u=parseInt(this._triggerForMinutes,10);isNaN(u)||(m.trigger_for_minutes=u)}}else if(this._triggerType==="counter"){if(this._triggerTargetValue){let u=parseFloat(this._triggerTargetValue);isNaN(u)||(m.trigger_target_value=u)}m.trigger_delta_mode=this._triggerDeltaMode}else if(this._triggerType==="state_change"){if(this._triggerFromState&&(m.trigger_from_state=this._triggerFromState),this._triggerToState&&(m.trigger_to_state=this._triggerToState),this._triggerTargetChanges){let u=parseInt(this._triggerTargetChanges,10);isNaN(u)||(m.trigger_target_changes=u)}}else if(this._triggerType==="runtime"&&this._triggerRuntimeHours){let u=parseFloat(this._triggerRuntimeHours);isNaN(u)||(m.trigger_runtime_hours=u)}t.trigger_config=m}else this._taskId&&(t.trigger_config=null);if(this.scheduleTimeEnabled&&this._scheduleType==="time_based"){let h=this._scheduleTime.trim();t.schedule_time=/^([01]\d|2[0-3]):[0-5]\d$/.test(h)?h:null}if(this.checklistsEnabled){let h=this._checklistText.split(`
`).map(m=>m.trim()).filter(Boolean).slice(0,100);t.checklist=h.length?h:null}if(this.completionActionsEnabled){let h=this._actionService.trim();if(h&&/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(h)){let I={service:h},F=this._actionTargetEntity.trim();F&&(I.target={entity_id:F});let w=this._buildActionData();Object.keys(w).length>0&&(I.data=w),t.on_complete_action=I}else t.on_complete_action=null;let m={};this._qcNotes.trim()&&(m.notes=this._qcNotes.trim());let u=parseFloat(this._qcCost);!isNaN(u)&&u>=0&&(m.cost=u);let y=parseInt(this._qcDuration,10);!isNaN(y)&&y>=0&&(m.duration=y),this._qcFeedback&&(m.feedback=this._qcFeedback),t.quick_complete_defaults=Object.keys(m).length?m:null}let i=await this.hass.connection.sendMessagePromise(t),o=this._taskId||i?.task_id,_=this._environmentalEntity!==this._environmentalInitial||this._environmentalAttribute!==this._environmentalAttributeInitial;if(o&&this._scheduleType==="sensor_based"&&_)try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/set_environmental_entity",entry_id:this._entryId,task_id:o,environmental_entity:this._environmentalEntity||null,environmental_attribute:this._environmentalAttribute||null}),this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute}catch{}this._open=!1,this.dispatchEvent(new CustomEvent("task-saved"))}catch(t){this._error=k(t,this._lang,s("save_error",this._lang))}finally{this._loading=!1}}}_close(){this._open=!1}_renderTriggerFields(){if(this._scheduleType!=="sensor_based")return c;let t=this._lang,e=this._triggerType==="compound";return r`
<h3>${s("trigger_configuration",t)}</h3>
<div class="select-row">
<label>${s("trigger_type",t)}</label>
@@ -284,7 +284,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
<ms-textfield
label="${s("entity_id",t)} (${s("comma_separated",t)})"
.value=${this._triggerEntityIds.length>0?this._triggerEntityIds.join(", "):this._triggerEntityId}
@input=${i=>{let u=i.target.value.split(",").map(_=>_.trim()).filter(Boolean);this._triggerEntityId=u[0]||"",this._triggerEntityIds=u,u[0]&&this._fetchEntityAttributes(u[0])}}
@input=${i=>{let _=i.target.value.split(",").map(h=>h.trim()).filter(Boolean);this._triggerEntityId=_[0]||"",this._triggerEntityIds=_,_[0]&&this._fetchEntityAttributes(_[0])}}
></ms-textfield>
${this._triggerEntityIds.length>1?r`
<div class="select-row">
@@ -365,15 +365,15 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
<ms-textfield
label="${s("entity_id",i)} (${s("comma_separated",i)})"
.value=${t.entityIds}
@input=${u=>this._patchCondition(e,{entityIds:u.target.value})}
@input=${_=>this._patchCondition(e,{entityIds:_.target.value})}
></ms-textfield>
<div class="select-row">
<label>${s("trigger_type",i)}</label>
<select
.value=${t.type}
@change=${u=>this._patchCondition(e,{type:u.target.value})}
@change=${_=>this._patchCondition(e,{type:_.target.value})}
>
${st.map(u=>r`<option value=${u} ?selected=${u===t.type}>${s(u,i)}</option>`)}
${at.map(_=>r`<option value=${_} ?selected=${_===t.type}>${s(_,i)}</option>`)}
</select>
</div>
${this._renderConditionTypeFields(t,e)}
@@ -412,7 +412,42 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
>
${["days","weeks","months","years"].map(e=>r`<option value=${e} ?selected=${e===this._intervalUnit}>${s("unit_"+e,t)}</option>`)}
</select>
</div>`}_toggleWeekday(t){this._weekdays=this._weekdays.includes(t)?this._weekdays.filter(e=>e!==t):[...this._weekdays,t]}_buildSchedule(){let t=i=>{let o=parseInt(this._calOffset,10)||0;return o&&(i.offset=Math.max(-15,Math.min(o,15))),i};if(this._scheduleType==="weekdays")return t({kind:"weekdays",weekdays:[...this._weekdays].sort((i,o)=>i-o)});if(this._scheduleType==="nth_weekday")return t({kind:"nth_weekday",nth:parseInt(this._nth,10),weekday:parseInt(this._nthWeekday,10)});let e={kind:"day_of_month",day:this._domLastDay?-1:parseInt(this._domDay,10)||1};return this._domBusiness&&(e.business=!0),t(e)}_renderCalendarFields(){let t=this._lang,e=At(t);if(this._scheduleType==="weekdays")return r`
</div>`}_toggleWeekday(t){this._weekdays=this._weekdays.includes(t)?this._weekdays.filter(e=>e!==t):[...this._weekdays,t]}_buildSchedule(){let t=i=>{let o=parseInt(this._calOffset,10)||0;return o&&(i.offset=Math.max(-15,Math.min(o,15))),i};if(this._scheduleType==="weekdays")return t({kind:"weekdays",weekdays:[...this._weekdays].sort((i,o)=>i-o)});if(this._scheduleType==="nth_weekday")return t({kind:"nth_weekday",nth:parseInt(this._nth,10),weekday:parseInt(this._nthWeekday,10)});let e={kind:"day_of_month",day:this._domLastDay?-1:parseInt(this._domDay,10)||1};return this._domBusiness&&(e.business=!0),t(e)}_recurrenceExtras(){let t={};if(this._seasonMonths.length&&(t.season_months=[...this._seasonMonths].sort((e,i)=>e-i)),this._endsMode==="count"){let e=parseInt(this._endsCount,10);e>=1&&(t.ends={count:e})}else this._endsMode==="until"&&this._endsUntil&&(t.ends={until:this._endsUntil});return t}_toggleSeasonMonth(t){this._seasonMonths=this._seasonMonths.includes(t)?this._seasonMonths.filter(e=>e!==t):[...this._seasonMonths,t]}_renderRecurrenceExtras(){let t=this._lang;if(!(this._scheduleType==="time_based"||st.includes(this._scheduleType)))return c;let i=Lt(t);return r`
<label class="field-label">${s("season_window_label",t)}</label>
<div class="field-help">${s("season_window_hint",t)}</div>
<div class="weekday-chips season-chips">
${i.map((o,_)=>r`
<button
type="button"
class="season-chip ${this._seasonMonths.includes(_+1)?"selected":""}"
@click=${()=>this._toggleSeasonMonth(_+1)}
>${o}</button>`)}
</div>
<label class="field-label">${s("series_end_label",t)}</label>
<div class="select-row">
<select .value=${this._endsMode}
@change=${o=>this._endsMode=o.target.value}>
<option value="never" ?selected=${this._endsMode==="never"}>${s("series_end_never",t)}</option>
<option value="count" ?selected=${this._endsMode==="count"}>${s("series_end_after_count",t)}</option>
<option value="until" ?selected=${this._endsMode==="until"}>${s("series_end_until",t)}</option>
</select>
</div>
${this._endsMode==="count"?r`
<ms-textfield
label="${s("series_end_count_label",t)}"
type="number" min="1"
.value=${this._endsCount}
@input=${o=>this._endsCount=o.target.value}
></ms-textfield>`:c}
${this._endsMode==="until"?r`
<ms-textfield
label="${s("series_end_until_label",t)}"
type="date"
.value=${this._endsUntil}
@input=${o=>this._endsUntil=o.target.value}
></ms-textfield>`:c}
`}_renderCalendarFields(){let t=this._lang,e=At(t);if(this._scheduleType==="weekdays")return r`
<label class="field-label">${s("recurrence_on_days",t)}</label>
<div class="weekday-chips">
${e.map((i,o)=>r`
@@ -426,13 +461,13 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
<div class="select-row">
<label>${s("recurrence_occurrence",t)}</label>
<select .value=${this._nth} @change=${o=>this._nth=o.target.value}>
${i.map(([o,u])=>r`<option value=${o} ?selected=${o===this._nth}>${u}</option>`)}
${i.map(([o,_])=>r`<option value=${o} ?selected=${o===this._nth}>${_}</option>`)}
</select>
</div>
<div class="select-row">
<label>${s("recurrence_weekday",t)}</label>
<select .value=${this._nthWeekday} @change=${o=>this._nthWeekday=o.target.value}>
${e.map((o,u)=>r`<option value=${String(u)} ?selected=${String(u)===this._nthWeekday}>${o}</option>`)}
${e.map((o,_)=>r`<option value=${String(_)} ?selected=${String(_)===this._nthWeekday}>${o}</option>`)}
</select>
</div>
${this._renderCalOffsetField()}`}return this._scheduleType==="day_of_month"?r`
@@ -557,7 +592,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
.value=${this._type}
@change=${i=>this._type=i.target.value}
>
${yt.map(i=>r`<option value=${i} ?selected=${i===this._type}>${s(i,t)}</option>`)}
${$t.map(i=>r`<option value=${i} ?selected=${i===this._type}>${s(i,t)}</option>`)}
</select>
</div>
${this._type==="reading"?r`
@@ -593,7 +628,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
.value=${this._scheduleType}
@change=${i=>this._scheduleType=i.target.value}
>
${$t.map(i=>r`<option value=${i} ?selected=${i===this._scheduleType}>${s(i,t)}</option>`)}
${kt.map(i=>r`<option value=${i} ?selected=${i===this._scheduleType}>${s(i,t)}</option>`)}
</select>
</div>
${this._scheduleType==="time_based"?r`
@@ -633,6 +668,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
@input=${i=>this._dueDate=i.target.value}
></ms-textfield>
`:c}
${this._renderRecurrenceExtras()}
<ms-textfield
label="${s("warning_days",t)}"
type="number"
@@ -780,7 +816,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
</ha-button>
</div>
</ha-dialog>
`}};h.styles=x`
`}};p.styles=$`
.dialog-title {
font-size: 18px;
font-weight: 500;
@@ -974,6 +1010,20 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
color: var(--text-primary-color, #fff);
border-color: var(--primary-color, #03a9f4);
}
.season-chip {
padding: 6px 10px;
border: 1px solid var(--divider-color);
border-radius: 16px;
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
font-size: 13px;
cursor: pointer;
}
.season-chip.selected {
background: var(--primary-color, #03a9f4);
color: var(--text-primary-color, #fff);
border-color: var(--primary-color, #03a9f4);
}
.error {
color: var(--error-color, #f44336);
font-size: 13px;
@@ -985,7 +1035,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
font-size: 14px;
cursor: pointer;
}
`,a([g({attribute:!1})],h.prototype,"hass",2),a([g({type:Boolean,attribute:"checklists-enabled"})],h.prototype,"checklistsEnabled",2),a([g({type:Boolean,attribute:"schedule-time-enabled"})],h.prototype,"scheduleTimeEnabled",2),a([g({type:Boolean,attribute:"completion-actions-enabled"})],h.prototype,"completionActionsEnabled",2),a([g({type:Number,attribute:"default-warning-days"})],h.prototype,"defaultWarningDays",2),a([l()],h.prototype,"_open",2),a([l()],h.prototype,"_loading",2),a([l()],h.prototype,"_error",2),a([l()],h.prototype,"_entryId",2),a([l()],h.prototype,"_taskId",2),a([l()],h.prototype,"_objectChoices",2),a([l()],h.prototype,"_name",2),a([l()],h.prototype,"_type",2),a([l()],h.prototype,"_scheduleType",2),a([l()],h.prototype,"_intervalDays",2),a([l()],h.prototype,"_intervalUnit",2),a([l()],h.prototype,"_dueDate",2),a([l()],h.prototype,"_warningDays",2),a([l()],h.prototype,"_earliestCompletionDays",2),a([l()],h.prototype,"_intervalAnchor",2),a([l()],h.prototype,"_weekdays",2),a([l()],h.prototype,"_nth",2),a([l()],h.prototype,"_nthWeekday",2),a([l()],h.prototype,"_domDay",2),a([l()],h.prototype,"_domLastDay",2),a([l()],h.prototype,"_domBusiness",2),a([l()],h.prototype,"_calOffset",2),a([l()],h.prototype,"_notes",2),a([l()],h.prototype,"_documentationUrl",2),a([l()],h.prototype,"_customIcon",2),a([l()],h.prototype,"_priority",2),a([l()],h.prototype,"_labels",2),a([l()],h.prototype,"_enabled",2),a([l()],h.prototype,"_triggerEntityId",2),a([l()],h.prototype,"_triggerEntityIds",2),a([l()],h.prototype,"_triggerEntityLogic",2),a([l()],h.prototype,"_triggerAttribute",2),a([l()],h.prototype,"_triggerType",2),a([l()],h.prototype,"_triggerAbove",2),a([l()],h.prototype,"_triggerBelow",2),a([l()],h.prototype,"_triggerForMinutes",2),a([l()],h.prototype,"_triggerTargetValue",2),a([l()],h.prototype,"_triggerDeltaMode",2),a([l()],h.prototype,"_autoCompleteOnRecovery",2),a([l()],h.prototype,"_triggerFromState",2),a([l()],h.prototype,"_triggerToState",2),a([l()],h.prototype,"_triggerTargetChanges",2),a([l()],h.prototype,"_triggerRuntimeHours",2),a([l()],h.prototype,"_compoundLogic",2),a([l()],h.prototype,"_compoundConditions",2),a([l()],h.prototype,"_suggestedAttributes",2),a([l()],h.prototype,"_availableAttributes",2),a([l()],h.prototype,"_entityDomain",2),a([l()],h.prototype,"_lastPerformed",2),a([l()],h.prototype,"_nfcTagId",2),a([l()],h.prototype,"_readingUnit",2),a([l()],h.prototype,"_availableTags",2),a([l()],h.prototype,"_responsibleUserId",2),a([l()],h.prototype,"_assigneePool",2),a([l()],h.prototype,"_rotationStrategy",2),a([l()],h.prototype,"_availableUsers",2),a([l()],h.prototype,"_checklistText",2),a([l()],h.prototype,"_scheduleTime",2),a([l()],h.prototype,"_actionService",2),a([l()],h.prototype,"_actionTargetEntity",2),a([l()],h.prototype,"_actionData",2),a([l()],h.prototype,"_actionDataJsonFallback",2),a([l()],h.prototype,"_actionTesting",2),a([l()],h.prototype,"_actionTestResult",2),a([l()],h.prototype,"_actionTestError",2),a([l()],h.prototype,"_qcNotes",2),a([l()],h.prototype,"_qcCost",2),a([l()],h.prototype,"_qcDuration",2),a([l()],h.prototype,"_qcFeedback",2),a([l()],h.prototype,"_environmentalEntity",2),a([l()],h.prototype,"_environmentalAttribute",2);customElements.get("maintenance-task-dialog")||customElements.define("maintenance-task-dialog",h);var v=class extends ${constructor(){super(...arguments);this.entryId="";this.taskId="";this.taskName="";this.lang="en";this.checklist=[];this.adaptiveEnabled=!1;this.taskType="";this.readingUnit="";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=""}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="")}_toggleCheck(t){let e=String(t);this._checklistState={...this._checklistState,[e]:!this._checklistState[e]}}_setFeedback(t){this._feedback=t}async _onPhotoInput(t){let e=t.target,i=e.files?.[0];if(e.value="",!!i){this._photoUploading=!0,this._error="";try{let o=new FormData;o.append("entry_id",this.entryId),o.append("tags","photo"),o.append("file",i,i.name);let u=await fetch("/api/maintenance_supporter/document/upload",{method:"POST",headers:{Authorization:`Bearer ${this.hass.auth?.data?.access_token??""}`},body:o});if(!u.ok){this._error=u.status===413?s("doc_too_large",this.lang):s("doc_upload_failed",this.lang);return}let _=await u.json();_.id&&(this._photoDocId=_.id,this._photoPreview=URL.createObjectURL(i))}catch{this._error=s("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 t={type:"maintenance_supporter/task/com Line truncated
`,a([g({attribute:!1})],p.prototype,"hass",2),a([g({type:Boolean,attribute:"checklists-enabled"})],p.prototype,"checklistsEnabled",2),a([g({type:Boolean,attribute:"schedule-time-enabled"})],p.prototype,"scheduleTimeEnabled",2),a([g({type:Boolean,attribute:"completion-actions-enabled"})],p.prototype,"completionActionsEnabled",2),a([g({type:Number,attribute:"default-warning-days"})],p.prototype,"defaultWarningDays",2),a([l()],p.prototype,"_open",2),a([l()],p.prototype,"_loading",2),a([l()],p.prototype,"_error",2),a([l()],p.prototype,"_entryId",2),a([l()],p.prototype,"_taskId",2),a([l()],p.prototype,"_objectChoices",2),a([l()],p.prototype,"_name",2),a([l()],p.prototype,"_type",2),a([l()],p.prototype,"_scheduleType",2),a([l()],p.prototype,"_intervalDays",2),a([l()],p.prototype,"_intervalUnit",2),a([l()],p.prototype,"_dueDate",2),a([l()],p.prototype,"_warningDays",2),a([l()],p.prototype,"_earliestCompletionDays",2),a([l()],p.prototype,"_intervalAnchor",2),a([l()],p.prototype,"_weekdays",2),a([l()],p.prototype,"_nth",2),a([l()],p.prototype,"_nthWeekday",2),a([l()],p.prototype,"_domDay",2),a([l()],p.prototype,"_domLastDay",2),a([l()],p.prototype,"_domBusiness",2),a([l()],p.prototype,"_calOffset",2),a([l()],p.prototype,"_seasonMonths",2),a([l()],p.prototype,"_endsMode",2),a([l()],p.prototype,"_endsCount",2),a([l()],p.prototype,"_endsUntil",2),a([l()],p.prototype,"_notes",2),a([l()],p.prototype,"_documentationUrl",2),a([l()],p.prototype,"_customIcon",2),a([l()],p.prototype,"_priority",2),a([l()],p.prototype,"_labels",2),a([l()],p.prototype,"_enabled",2),a([l()],p.prototype,"_triggerEntityId",2),a([l()],p.prototype,"_triggerEntityIds",2),a([l()],p.prototype,"_triggerEntityLogic",2),a([l()],p.prototype,"_triggerAttribute",2),a([l()],p.prototype,"_triggerType",2),a([l()],p.prototype,"_triggerAbove",2),a([l()],p.prototype,"_triggerBelow",2),a([l()],p.prototype,"_triggerForMinutes",2),a([l()],p.prototype,"_triggerTargetValue",2),a([l()],p.prototype,"_triggerDeltaMode",2),a([l()],p.prototype,"_autoCompleteOnRecovery",2),a([l()],p.prototype,"_triggerFromState",2),a([l()],p.prototype,"_triggerToState",2),a([l()],p.prototype,"_triggerTargetChanges",2),a([l()],p.prototype,"_triggerRuntimeHours",2),a([l()],p.prototype,"_compoundLogic",2),a([l()],p.prototype,"_compoundConditions",2),a([l()],p.prototype,"_suggestedAttributes",2),a([l()],p.prototype,"_availableAttributes",2),a([l()],p.prototype,"_entityDomain",2),a([l()],p.prototype,"_lastPerformed",2),a([l()],p.prototype,"_nfcTagId",2),a([l()],p.prototype,"_readingUnit",2),a([l()],p.prototype,"_availableTags",2),a([l()],p.prototype,"_responsibleUserId",2),a([l()],p.prototype,"_assigneePool",2),a([l()],p.prototype,"_rotationStrategy",2),a([l()],p.prototype,"_availableUsers",2),a([l()],p.prototype,"_checklistText",2),a([l()],p.prototype,"_scheduleTime",2),a([l()],p.prototype,"_actionService",2),a([l()],p.prototype,"_actionTargetEntity",2),a([l()],p.prototype,"_actionData",2),a([l()],p.prototype,"_actionDataJsonFallback",2),a([l()],p.prototype,"_actionTesting",2),a([l()],p.prototype,"_actionTestResult",2),a([l()],p.prototype,"_actionTestError",2),a([l()],p.prototype,"_qcNotes",2),a([l()],p.prototype,"_qcCost",2),a([l()],p.prototype,"_qcDuration",2),a([l()],p.prototype,"_qcFeedback",2),a([l()],p.prototype,"_environmentalEntity",2),a([l()],p.prototype,"_environmentalAttribute",2);customElements.get("maintenance-task-dialog")||customElements.define("maintenance-task-dialog",p);var v=class extends x{constructor(){super(...arguments);this.entryId="";this.taskId="";this.taskName="";this.lang="en";this.checklist=[];this.adaptiveEnabled=!1;this.taskType="";this.readingUnit="";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=""}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="")}_toggleCheck(t){let e=String(t);this._checklistState={...this._checklistState,[e]:!this._checklistState[e]}}_setFeedback(t){this._feedback=t}async _onPhotoInput(t){let e=t.target,i=e.files?.[0];if(e.value="",!!i){this._photoUploading=!0,this._error="";try{let o=new FormData;o.append("entry_id",this.entryId),o.append("tags","photo"),o.append("file",i,i.name);let _=await fetch("/api/maintenance_supporter/document/upload",{method:"POST",headers:{Authorization:`Bearer ${this.hass.auth?.data?.access_token??""}`},body:o});if(!_.ok){this._error=_.status===413?s("doc_too_large",this.lang):s("doc_upload_failed",this.lang);return}let h=await _.json();h.id&&(this._photoDocId=h.id,this._photoPreview=URL.createObjectURL(i))}catch{this._error=s("doc_upload_failed",this.lang)}finally{this._photoUploading=!1}}}_removePhoto(){this._photoPreview&&URL.revokeObjectURL(this._photoPr Line truncated
<ha-dialog open @closed=${this._close}>
<div class="dialog-title">${s("complete_title",t)}${this.taskName}</div>
<div class="content">
@@ -1080,7 +1130,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
</ha-button>
</div>
</ha-dialog>
`}};v.styles=x`
`}};v.styles=$`
.dialog-title {
font-size: 18px;
font-weight: 500;
@@ -1219,7 +1269,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
color: var(--text-primary-color, #fff);
border-color: var(--primary-color);
}
`,a([g({attribute:!1})],v.prototype,"hass",2),a([g()],v.prototype,"entryId",2),a([g()],v.prototype,"taskId",2),a([g()],v.prototype,"taskName",2),a([g()],v.prototype,"lang",2),a([g({type:Array})],v.prototype,"checklist",2),a([g({type:Boolean})],v.prototype,"adaptiveEnabled",2),a([g()],v.prototype,"taskType",2),a([g()],v.prototype,"readingUnit",2),a([l()],v.prototype,"_open",2),a([l()],v.prototype,"_notes",2),a([l()],v.prototype,"_cost",2),a([l()],v.prototype,"_duration",2),a([l()],v.prototype,"_loading",2),a([l()],v.prototype,"_error",2),a([l()],v.prototype,"_checklistState",2),a([l()],v.prototype,"_feedback",2),a([l()],v.prototype,"_photoDocId",2),a([l()],v.prototype,"_photoPreview",2),a([l()],v.prototype,"_photoUploading",2),a([l()],v.prototype,"_readingValue",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",v);var R=class extends ${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(t){this._draft={...t},this._originalSnapshot={...t},this._error="",this._open=!0}close(){this._open=!1,this._error="",this._draft=null,this._originalSnapshot=null}_set(t,e){this._draft&&(this._draft={...this._draft,[t]:e})}async _save(){if(!(!this._draft||!this._originalSnapshot)){this._saving=!0,this._error="";try{let t={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&&(t.timestamp=this._draft.timestamp),this._draft.notes!==this._originalSnapshot.notes&&(t.notes=this._draft.notes),this._draft.cost!==this._originalSnapshot.cost&&(t.cost=this._draft.cost),this._draft.duration!==this._originalSnapshot.duration&&(t.duration=this._draft.duration),this._draft.completed_by!==this._originalSnapshot.completed_by&&(t.completed_by=this._draft.completed_by),Object.keys(t).filter(i=>!["type","entry_id","task_id","original_timestamp"].includes(i)).length===0){this.close();return}await this.hass.connection.sendMessagePromise(t),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(t){this._error=k(t,this._lang)}finally{this._saving=!1}}}render(){if(!this._open||!this._draft)return c;let t=this._lang,e=this._draft;return r`
`,a([g({attribute:!1})],v.prototype,"hass",2),a([g()],v.prototype,"entryId",2),a([g()],v.prototype,"taskId",2),a([g()],v.prototype,"taskName",2),a([g()],v.prototype,"lang",2),a([g({type:Array})],v.prototype,"checklist",2),a([g({type:Boolean})],v.prototype,"adaptiveEnabled",2),a([g()],v.prototype,"taskType",2),a([g()],v.prototype,"readingUnit",2),a([l()],v.prototype,"_open",2),a([l()],v.prototype,"_notes",2),a([l()],v.prototype,"_cost",2),a([l()],v.prototype,"_duration",2),a([l()],v.prototype,"_loading",2),a([l()],v.prototype,"_error",2),a([l()],v.prototype,"_checklistState",2),a([l()],v.prototype,"_feedback",2),a([l()],v.prototype,"_photoDocId",2),a([l()],v.prototype,"_photoPreview",2),a([l()],v.prototype,"_photoUploading",2),a([l()],v.prototype,"_readingValue",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",v);var R=class extends x{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(t){this._draft={...t},this._originalSnapshot={...t},this._error="",this._open=!0}close(){this._open=!1,this._error="",this._draft=null,this._originalSnapshot=null}_set(t,e){this._draft&&(this._draft={...this._draft,[t]:e})}async _save(){if(!(!this._draft||!this._originalSnapshot)){this._saving=!0,this._error="";try{let t={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&&(t.timestamp=this._draft.timestamp),this._draft.notes!==this._originalSnapshot.notes&&(t.notes=this._draft.notes),this._draft.cost!==this._originalSnapshot.cost&&(t.cost=this._draft.cost),this._draft.duration!==this._originalSnapshot.duration&&(t.duration=this._draft.duration),this._draft.completed_by!==this._originalSnapshot.completed_by&&(t.completed_by=this._draft.completed_by),Object.keys(t).filter(i=>!["type","entry_id","task_id","original_timestamp"].includes(i)).length===0){this.close();return}await this.hass.connection.sendMessagePromise(t),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(t){this._error=k(t,this._lang)}finally{this._saving=!1}}}render(){if(!this._open||!this._draft)return c;let t=this._lang,e=this._draft;return r`
<div class="backdrop" @click=${this.close}></div>
<div class="dialog" role="dialog" aria-modal="true">
<h2>${s("history_edit_title",t)||"Edit history entry"}</h2>
@@ -1264,7 +1314,7 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
</button>
</div>
</div>
`}};R.styles=x`
`}};R.styles=$`
:host { display: contents; }
.backdrop {
position: fixed; inset: 0;
@@ -1326,33 +1376,33 @@ import{a as x,b as r,c as M,d as c,e as $,f as g,g as l,i as V,j as s,k as Z,l a
background: rgba(211,47,47,0.1);
border-radius: 6px;
}
`,a([g({attribute:!1})],R.prototype,"hass",2),a([l()],R.prototype,"_open",2),a([l()],R.prototype,"_saving",2),a([l()],R.prototype,"_error",2),a([l()],R.prototype,"_draft",2);customElements.get("maintenance-history-edit-dialog")||customElements.define("maintenance-history-edit-dialog",R);function N(n){return n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function at(n){return!n.startsWith("data:image/svg+xml,")&&!n.startsWith("data:image/png;base64,")?"":N(n)}function Lt(n){return n.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var T=class extends ${constructor(){super(...arguments);this.lang="en";this._open=!1;this._loading=!1;this._error="";this._viewResult=null;this._completeResult=null;this._urlMode="companion";this._entryId="";this._taskId=null;this._objectName="";this._taskName="";this._generateSeq=0}openForObject(t,e){this._entryId=t,this._taskId=null,this._objectName=e,this._taskName="",this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._open=!0,this._generate()}openForTask(t,e,i,o){this._entryId=t,this._taskId=e,this._objectName=i,this._taskName=o,this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._open=!0,this._generate()}async _generate(){let t=++this._generateSeq;this._loading=!0,this._error="",this._viewResult=null,this._completeResult=null;try{let e={type:"maintenance_supporter/qr/generate",entry_id:this._entryId,url_mode:this._urlMode};this._taskId&&(e.task_id=this._taskId);let i=[this.hass.connection.sendMessagePromise({...e,action:"view"})];this._taskId&&i.push(this.hass.connection.sendMessagePromise({...e,action:"complete"}));let o=await Promise.all(i);if(t!==this._generateSeq)return;this._viewResult=o[0],o.length>1&&(this._completeResult=o[1])}catch(e){if(t!==this._generateSeq)return;let i=e?.code,o=e?.message;this._error=i==="no_url"||typeof o=="string"&&o.includes("No Home Assistant URL")?s("qr_error_no_url",this.lang):s("qr_error",this.lang)}finally{t===this._generateSeq&&(this._loading=!1)}}_setUrlMode(t){this._urlMode!==t&&(this._urlMode=t,this._generate())}_print(){if(!this._viewResult)return;let t=this._viewResult,e=t.label.task_name?`${t.label.object_name} \u2014 ${t.label.task_name}`:t.label.object_name,i=[t.label.manufacturer,t.label.model].filter(Boolean).join(" "),o=window.open("","_blank","width=600,height=500");if(!o)return;let u=this.lang||"en",_=N(e),p=N(i),m=!!this._completeResult,y=N(s("qr_action_view",u)),I=N(s("qr_action_complete",u));o.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
<title>${_}</title>
`,a([g({attribute:!1})],R.prototype,"hass",2),a([l()],R.prototype,"_open",2),a([l()],R.prototype,"_saving",2),a([l()],R.prototype,"_error",2),a([l()],R.prototype,"_draft",2);customElements.get("maintenance-history-edit-dialog")||customElements.define("maintenance-history-edit-dialog",R);function U(n){return n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function rt(n){return!n.startsWith("data:image/svg+xml,")&&!n.startsWith("data:image/png;base64,")?"":U(n)}function Ct(n){return n.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var T=class extends x{constructor(){super(...arguments);this.lang="en";this._open=!1;this._loading=!1;this._error="";this._viewResult=null;this._completeResult=null;this._urlMode="companion";this._entryId="";this._taskId=null;this._objectName="";this._taskName="";this._generateSeq=0}openForObject(t,e){this._entryId=t,this._taskId=null,this._objectName=e,this._taskName="",this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._open=!0,this._generate()}openForTask(t,e,i,o){this._entryId=t,this._taskId=e,this._objectName=i,this._taskName=o,this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._open=!0,this._generate()}async _generate(){let t=++this._generateSeq;this._loading=!0,this._error="",this._viewResult=null,this._completeResult=null;try{let e={type:"maintenance_supporter/qr/generate",entry_id:this._entryId,url_mode:this._urlMode};this._taskId&&(e.task_id=this._taskId);let i=[this.hass.connection.sendMessagePromise({...e,action:"view"})];this._taskId&&i.push(this.hass.connection.sendMessagePromise({...e,action:"complete"}));let o=await Promise.all(i);if(t!==this._generateSeq)return;this._viewResult=o[0],o.length>1&&(this._completeResult=o[1])}catch(e){if(t!==this._generateSeq)return;let i=e?.code,o=e?.message;this._error=i==="no_url"||typeof o=="string"&&o.includes("No Home Assistant URL")?s("qr_error_no_url",this.lang):s("qr_error",this.lang)}finally{t===this._generateSeq&&(this._loading=!1)}}_setUrlMode(t){this._urlMode!==t&&(this._urlMode=t,this._generate())}_print(){if(!this._viewResult)return;let t=this._viewResult,e=t.label.task_name?`${t.label.object_name} \u2014 ${t.label.task_name}`:t.label.object_name,i=[t.label.manufacturer,t.label.model].filter(Boolean).join(" "),o=window.open("","_blank","width=600,height=500");if(!o)return;let _=this.lang||"en",h=U(e),m=U(i),u=!!this._completeResult,y=U(s("qr_action_view",_)),I=U(s("qr_action_complete",_));o.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
<title>${h}</title>
<style>
body{font-family:sans-serif;text-align:center;padding:20px}
h2{margin:0 0 4px}
.sub{color:#666;font-size:14px;margin-bottom:16px}
.qr-row{display:flex;justify-content:center;gap:24px;margin:12px 0}
.qr-col{display:flex;flex-direction:column;align-items:center;gap:6px}
.qr-col img{width:${m?"200px":"280px"}}
.qr-col img{width:${u?"200px":"280px"}}
.qr-label{font-size:13px;font-weight:500;color:#333}
.url{font-size:10px;color:#999;word-break:break-all;margin-top:8px;max-width:480px}
</style></head><body>
<h2>${_}</h2>
${p?`<div class="sub">${p}</div>`:""}
<h2>${h}</h2>
${m?`<div class="sub">${m}</div>`:""}
<div class="qr-row">
<div class="qr-col">
<img src="${at(this._viewResult.svg_data_uri)}" alt="QR Info" />
<img src="${rt(this._viewResult.svg_data_uri)}" alt="QR Info" />
<div class="qr-label">${y}</div>
</div>
${m?`<div class="qr-col">
<img src="${at(this._completeResult.svg_data_uri)}" alt="QR Complete" />
${u?`<div class="qr-col">
<img src="${rt(this._completeResult.svg_data_uri)}" alt="QR Complete" />
<div class="qr-label">${I}</div>
</div>`:""}
</div>
<div class="url">${N(this._viewResult.url)}</div>
<div class="url">${U(this._viewResult.url)}</div>
<script>setTimeout(()=>window.print(),300)<\/script>
</body></html>`),o.document.close()}_downloadSvg(t,e){let i=decodeURIComponent(t.svg_data_uri.replace("data:image/svg+xml,","")),o=new Blob([i],{type:"image/svg+xml"}),u=URL.createObjectURL(o),_=document.createElement("a");_.href=u;let p=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;_.download=`qr-${Lt(p)}-${e}.svg`,_.click(),URL.revokeObjectURL(u)}_close(){this._open=!1,this._viewResult=null,this._completeResult=null,this._error="",this._loading=!1}render(){if(!this._open)return r``;let t=this.lang||this.hass?.language||"en",e=this._taskName?`${s("qr_code",t)}: ${this._objectName} \u2014 ${this._taskName}`:`${s("qr_code",t)}: ${this._objectName}`,i=!!this._viewResult;return r`
</body></html>`),o.document.close()}_downloadSvg(t,e){let i=decodeURIComponent(t.svg_data_uri.replace("data:image/svg+xml,","")),o=new Blob([i],{type:"image/svg+xml"}),_=URL.createObjectURL(o),h=document.createElement("a");h.href=_;let m=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;h.download=`qr-${Ct(m)}-${e}.svg`,h.click(),URL.revokeObjectURL(_)}_close(){this._open=!1,this._viewResult=null,this._completeResult=null,this._error="",this._loading=!1}render(){if(!this._open)return r``;let t=this.lang||this.hass?.language||"en",e=this._taskName?`${s("qr_code",t)}: ${this._objectName} \u2014 ${this._taskName}`:`${s("qr_code",t)}: ${this._objectName}`,i=!!this._viewResult;return r`
<ha-dialog open @closed=${this._close}>
<div class="dialog-title">${e}</div>
<div class="content">
@@ -1413,7 +1463,7 @@ ${p?`<div class="sub">${p}</div>`:""}
</ha-button>
</div>
</ha-dialog>
`}};T.styles=x`
`}};T.styles=$`
.dialog-title {
font-size: 18px;
font-weight: 500;
@@ -1531,26 +1581,26 @@ ${p?`<div class="sub">${p}</div>`:""}
color: var(--text-primary-color, #fff);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
}
`,a([g({attribute:!1})],T.prototype,"hass",2),a([g()],T.prototype,"lang",2),a([l()],T.prototype,"_open",2),a([l()],T.prototype,"_loading",2),a([l()],T.prototype,"_error",2),a([l()],T.prototype,"_viewResult",2),a([l()],T.prototype,"_completeResult",2),a([l()],T.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",T);function rt(n,d){let t=n.interval_analysis,e=t?.weibull_beta,i=t?.weibull_eta;if(e==null||i==null||i<=0)return c;let o=n.interval_days??0,u=n.suggested_interval??o;return r`
`,a([g({attribute:!1})],T.prototype,"hass",2),a([g()],T.prototype,"lang",2),a([l()],T.prototype,"_open",2),a([l()],T.prototype,"_loading",2),a([l()],T.prototype,"_error",2),a([l()],T.prototype,"_viewResult",2),a([l()],T.prototype,"_completeResult",2),a([l()],T.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",T);function nt(n,d){let t=n.interval_analysis,e=t?.weibull_beta,i=t?.weibull_eta;if(e==null||i==null||i<=0)return c;let o=n.interval_days??0,_=n.suggested_interval??o;return r`
<div class="weibull-section">
<div class="weibull-title">
<ha-svg-icon aria-hidden="true" path="M3,14L3.5,14.07L8.07,9.5C7.89,8.85 8.06,8.11 8.59,7.59C9.37,6.8 10.63,6.8 11.41,7.59C11.94,8.11 12.11,8.85 11.93,9.5L14.5,12.07L15,12C15.18,12 15.35,12 15.5,12.07L19.07,8.5C19,8.35 19,8.18 19,8A2,2 0 0,1 21,6A2,2 0 0,1 23,8A2,2 0 0,1 21,10C20.82,10 20.65,10 20.5,9.93L16.93,13.5C17,13.65 17,13.82 17,14A2,2 0 0,1 15,16A2,2 0 0,1 13,14L13.07,13.5L10.5,10.93C10.18,11 9.82,11 9.5,10.93L4.93,15.5L5,16A2,2 0 0,1 3,18A2,2 0 0,1 1,16A2,2 0 0,1 3,14Z"></ha-svg-icon>
${s("weibull_reliability_curve",d)}
${Ct(e,d)}
${Ht(e,d)}
</div>
${Ht(e,i,o,u,d)}
${St(t,d)}
${t?.confidence_interval_low!=null?Rt(t,n,d):c}
${St(e,i,o,_,d)}
${Rt(t,d)}
${t?.confidence_interval_low!=null?Pt(t,n,d):c}
</div>
`}function Ct(n,d){let t,e,i;return n<.8?(t="early_failures",e="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z",i="beta_early_failures"):n<=1.2?(t="random_failures",e="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M13,17H11V15H13V17M13,13H11V7H13V13Z",i="beta_random_failures"):n<=3.5?(t="wear_out",e="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M12,6A6,6 0 0,1 18,12H12V6Z",i="beta_wear_out"):(t="highly_predictable",e="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z",i="beta_highly_predictable"),r`
`}function Ht(n,d){let t,e,i;return n<.8?(t="early_failures",e="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z",i="beta_early_failures"):n<=1.2?(t="random_failures",e="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M13,17H11V15H13V17M13,13H11V7H13V13Z",i="beta_random_failures"):n<=3.5?(t="wear_out",e="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M12,6A6,6 0 0,1 18,12H12V6Z",i="beta_wear_out"):(t="highly_predictable",e="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M11,16.5L18,9.5L16.59,8.09L11,13.67L7.91,10.59L6.5,12L11,16.5Z",i="beta_highly_predictable"),r`
<span class="beta-badge ${t}">
<ha-svg-icon path="${e}"></ha-svg-icon>
${s(i,d)} (\u03B2=${n.toFixed(2)})
</span>
`}function Ht(n,d,t,e,i){let w=Math.max(t,e,d,1)*1.3,H=50,S=[];for(let A=0;A<=H;A++){let L=A/H*w,vt=1-Math.exp(-Math.pow(L/d,n)),ft=32+L/w*260,bt=136-vt*128;S.push([ft,bt])}let D=S.map(([A,L])=>`${A.toFixed(1)},${L.toFixed(1)}`).join(" "),K="M32,136 "+S.map(([A,L])=>`L${A.toFixed(1)},${L.toFixed(1)}`).join(" ")+` L${S[H][0].toFixed(1)},136 Z`,q=32+t/w*260,j=1-Math.exp(-Math.pow(t/d,n)),O=136-j*128,mt=((1-j)*100).toFixed(0),Y=32+e/w*260,gt=[0,.25,.5,.75,1];return r`
`}function St(n,d,t,e,i){let w=Math.max(t,e,d,1)*1.3,H=50,S=[];for(let A=0;A<=H;A++){let L=A/H*w,ft=1-Math.exp(-Math.pow(L/d,n)),bt=32+L/w*260,yt=136-ft*128;S.push([bt,yt])}let D=S.map(([A,L])=>`${A.toFixed(1)},${L.toFixed(1)}`).join(" "),K="M32,136 "+S.map(([A,L])=>`L${A.toFixed(1)},${L.toFixed(1)}`).join(" ")+` L${S[H][0].toFixed(1)},136 Z`,z=32+t/w*260,j=1-Math.exp(-Math.pow(t/d,n)),O=136-j*128,gt=((1-j)*100).toFixed(0),Y=32+e/w*260,vt=[0,.25,.5,.75,1];return r`
<div class="weibull-chart">
<svg viewBox="0 0 ${300} ${160}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${s("chart_weibull",i)}">
${gt.map(A=>{let L=136-A*128;return M`
${vt.map(A=>{let L=136-A*128;return q`
<line x1="${32}" y1="${L.toFixed(1)}" x2="${292}" y2="${L.toFixed(1)}"
stroke="var(--divider-color)" stroke-width="0.5" stroke-dasharray="${A===.5?"4,3":c}" />
<text x="${28}" y="${(L+3).toFixed(1)}" fill="var(--secondary-text-color)"
@@ -1565,16 +1615,16 @@ ${p?`<div class="sub">${p}</div>`:""}
<polyline points="${D}" fill="none"
stroke="var(--primary-color, #03a9f4)" stroke-width="2" />
${t>0?M`
<line x1="${q.toFixed(1)}" y1="${8}" x2="${q.toFixed(1)}" y2="${136 .toFixed(1)}"
${t>0?q`
<line x1="${z.toFixed(1)}" y1="${8}" x2="${z.toFixed(1)}" y2="${136 .toFixed(1)}"
stroke="var(--primary-color, #03a9f4)" stroke-width="1.5" stroke-dasharray="4,3" />
<circle cx="${q.toFixed(1)}" cy="${O.toFixed(1)}" r="3"
<circle cx="${z.toFixed(1)}" cy="${O.toFixed(1)}" r="3"
fill="var(--primary-color, #03a9f4)" />
<text x="${(q+4).toFixed(1)}" y="${(O-6).toFixed(1)}" fill="var(--primary-color, #03a9f4)"
font-size="9" font-weight="600">R=${mt}%</text>
<text x="${(z+4).toFixed(1)}" y="${(O-6).toFixed(1)}" fill="var(--primary-color, #03a9f4)"
font-size="9" font-weight="600">R=${gt}%</text>
`:c}
${e>0&&e!==t?M`
${e>0&&e!==t?q`
<line x1="${Y.toFixed(1)}" y1="${8}" x2="${Y.toFixed(1)}" y2="${136 .toFixed(1)}"
stroke="var(--success-color, #4caf50)" stroke-width="1.5" stroke-dasharray="4,3" />
`:c}
@@ -1590,7 +1640,7 @@ ${p?`<div class="sub">${p}</div>`:""}
${t>0?r`<span class="legend-item"><span class="legend-swatch" style="background:var(--primary-color, #03a9f4); opacity:0.5"></span> ${s("current_interval_marker",i)}</span>`:c}
${e>0&&e!==t?r`<span class="legend-item"><span class="legend-swatch" style="background:var(--success-color, #4caf50)"></span> ${s("recommended_marker",i)}</span>`:c}
</div>
`}function St(n,d){return r`
`}function Rt(n,d){return r`
<div class="weibull-info-row">
<div class="weibull-info-item">
<span>${s("characteristic_life",d)}</span>
@@ -1603,7 +1653,7 @@ ${p?`<div class="sub">${p}</div>`:""}
</div>
`:c}
</div>
`}function Rt(n,d,t){let e=n.confidence_interval_low,i=n.confidence_interval_high,o=d.suggested_interval??d.interval_days??0,u=d.interval_days??0,_=Math.max(0,e-5),m=i+5-_,y=(e-_)/m*100,I=(i-e)/m*100,z=(o-_)/m*100,w=u>0?(u-_)/m*100:-1;return r`
`}function Pt(n,d,t){let e=n.confidence_interval_low,i=n.confidence_interval_high,o=d.suggested_interval??d.interval_days??0,_=d.interval_days??0,h=Math.max(0,e-5),u=i+5-h,y=(e-h)/u*100,I=(i-e)/u*100,F=(o-h)/u*100,w=_>0?(_-h)/u*100:-1;return r`
<div class="confidence-range">
<div class="confidence-range-title">
${s("confidence_interval",t)}: ${o} ${s("days",t)} (${e}\u2013${i})
@@ -1611,14 +1661,14 @@ ${p?`<div class="sub">${p}</div>`:""}
<div class="confidence-bar">
<div class="confidence-fill" style="left:${y.toFixed(1)}%;width:${I.toFixed(1)}%"></div>
${w>=0?r`<div class="confidence-marker current" style="left:${w.toFixed(1)}%"></div>`:c}
<div class="confidence-marker recommended" style="left:${z.toFixed(1)}%"></div>
<div class="confidence-marker recommended" style="left:${F.toFixed(1)}%"></div>
</div>
<div class="confidence-labels">
<span class="confidence-text low">${s("confidence_conservative",t)} (${e}${s("days",t).charAt(0)})</span>
<span class="confidence-text high">${s("confidence_aggressive",t)} (${i}${s("days",t).charAt(0)})</span>
</div>
</div>
`}function nt(n,d,t){let e=n.degradation_trend!=null&&n.degradation_trend!=="insufficient_data",i=n.days_until_threshold!=null,o=n.environmental_factor!=null&&n.environmental_factor!==1;if(!e&&!i&&!o)return c;let u=n.degradation_trend==="rising"?"M16,6L18.29,8.29L13.41,13.17L9.41,9.17L2,16.59L3.41,18L9.41,12L13.41,16L19.71,9.71L22,12V6H16Z":n.degradation_trend==="falling"?"M16,18L18.29,15.71L13.41,10.83L9.41,14.83L2,7.41L3.41,6L9.41,12L13.41,8L19.71,14.29L22,12V18H16Z":"M22,12L18,8V11H3V13H18V16L22,12Z";return r`
`}function ot(n,d,t){let e=n.degradation_trend!=null&&n.degradation_trend!=="insufficient_data",i=n.days_until_threshold!=null,o=n.environmental_factor!=null&&n.environmental_factor!==1;if(!e&&!i&&!o)return c;let _=n.degradation_trend==="rising"?"M16,6L18.29,8.29L13.41,13.17L9.41,9.17L2,16.59L3.41,18L9.41,12L13.41,16L19.71,9.71L22,12V6H16Z":n.degradation_trend==="falling"?"M16,18L18.29,15.71L13.41,10.83L9.41,14.83L2,7.41L3.41,6L9.41,12L13.41,8L19.71,14.29L22,12V18H16Z":"M22,12L18,8V11H3V13H18V16L22,12Z";return r`
<div class="prediction-section">
${n.sensor_prediction_urgency?r`
<div class="prediction-urgency-banner">
@@ -1633,7 +1683,7 @@ ${p?`<div class="sub">${p}</div>`:""}
<div class="prediction-grid">
${e?r`
<div class="prediction-item">
<ha-svg-icon path="${u}"></ha-svg-icon>
<ha-svg-icon path="${_}"></ha-svg-icon>
<span class="prediction-label">${s("degradation_trend",d)}</span>
<span class="prediction-value ${n.degradation_trend}">${s("trend_"+n.degradation_trend,d)}</span>
${n.degradation_rate!=null?r`<span class="prediction-rate">${n.degradation_rate>0?"+":""}${Math.abs(n.degradation_rate)>=10?Math.round(n.degradation_rate).toLocaleString():n.degradation_rate.toFixed(1)} ${n.trigger_entity_info?.unit_of_measurement||""}/${s("day_short",d)}</span>`:c}
@@ -1644,7 +1694,7 @@ ${p?`<div class="sub">${p}</div>`:""}
<ha-svg-icon path="M12,20A7,7 0 0,1 5,13A7,7 0 0,1 12,6A7,7 0 0,1 19,13A7,7 0 0,1 12,20M12,4A9,9 0 0,0 3,13A9,9 0 0,0 12,22A9,9 0 0,0 21,13A9,9 0 0,0 12,4M12.5,8H11V14L15.75,16.85L16.5,15.62L12.5,13.25V8M7.88,3.39L6.6,1.86L2,5.71L3.29,7.24L7.88,3.39M22,5.72L17.4,1.86L16.11,3.39L20.71,7.25L22,5.72Z"></ha-svg-icon>
<span class="prediction-label">${s("days_until_threshold",d)}</span>
<span class="prediction-value prediction-days${n.days_until_threshold===0?" exceeded":n.sensor_prediction_urgency?" urgent":""}">${n.days_until_threshold===0?s("threshold_exceeded",d):"~"+Math.round(n.days_until_threshold)+" "+s("days",d)}</span>
${n.threshold_prediction_date?r`<span class="prediction-date">${U(n.threshold_prediction_date,d)}</span>`:c}
${n.threshold_prediction_date?r`<span class="prediction-date">${N(n.threshold_prediction_date,d)}</span>`:c}
${n.threshold_prediction_confidence?r`<span class="confidence-dot ${n.threshold_prediction_confidence}"></span>`:c}
</div>
`:c}
@@ -1653,12 +1703,12 @@ ${p?`<div class="sub">${p}</div>`:""}
<ha-svg-icon path="M15,13V5A3,3 0 0,0 12,2A3,3 0 0,0 9,5V13A5,5 0 0,0 7,17A5,5 0 0,0 12,22A5,5 0 0,0 17,17A5,5 0 0,0 15,13M12,4A1,1 0 0,1 13,5V8H11V5A1,1 0 0,1 12,4Z"></ha-svg-icon>
<span class="prediction-label">${s("environmental_adjustment",d)}</span>
<span class="prediction-value">${n.environmental_factor.toFixed(2)}x</span>
${n.environmental_entity?r`<span class="prediction-entity entity-link" @click=${_=>et(_,n.environmental_entity)}>${n.environmental_entity}</span>`:c}
${n.environmental_entity?r`<span class="prediction-entity entity-link" @click=${h=>et(h,n.environmental_entity)}>${n.environmental_entity}</span>`:c}
</div>
`:c}
</div>
</div>
`}function ot(n,d,t,e){let i=Math.max(n||1,d);return r`
`}function lt(n,d,t,e){let i=Math.max(n||1,d);return r`
<div class="interval-comparison">
<div class="interval-bar">
<div class="interval-label">
@@ -1676,14 +1726,14 @@ ${p?`<div class="sub">${p}</div>`:""}
style="width: ${Math.min(d/i*100,100)}%"></div>
</div>
</div>
`}var lt=["month_jan","month_feb","month_mar","month_apr","month_may","month_jun","month_jul","month_aug","month_sep","month_oct","month_nov","month_dec"];function dt(n,d,t){if(!t.seasonal||!n.seasonal_factor||n.seasonal_factor===1)return c;let e=lt.map(_=>s(_,d)),i=new Date().getMonth(),o=n.seasonal_factors||n.interval_analysis?.seasonal_factors||null,u=o&&o.length===12?o:e.map((_,p)=>{let m=n.seasonal_factor||1,y=Math.sin((p-6)*Math.PI/6)*.3;return Math.max(.7,Math.min(1.3,m+y))});return r`
`}var dt=["month_jan","month_feb","month_mar","month_apr","month_may","month_jun","month_jul","month_aug","month_sep","month_oct","month_nov","month_dec"];function ct(n,d,t){if(!t.seasonal||!n.seasonal_factor||n.seasonal_factor===1)return c;let e=dt.map(h=>s(h,d)),i=new Date().getMonth(),o=n.seasonal_factors||n.interval_analysis?.seasonal_factors||null,_=o&&o.length===12?o:e.map((h,m)=>{let u=n.seasonal_factor||1,y=Math.sin((m-6)*Math.PI/6)*.3;return Math.max(.7,Math.min(1.3,u+y))});return r`
<div class="seasonal-card-compact">
<h4>${s("seasonal_awareness",d)}</h4>
<div class="seasonal-mini-chart">
${u.map((_,p)=>{let m=_*40,y=_<.9?"low":_>1.1?"high":"normal";return r`
<div class="seasonal-bar ${y} ${p===i?"current":""}"
style="height: ${m}px"
title="${e[p]}: ${_.toFixed(2)}x">
${_.map((h,m)=>{let u=h*40,y=h<.9?"low":h>1.1?"high":"normal";return r`
<div class="seasonal-bar ${y} ${m===i?"current":""}"
style="height: ${u}px"
title="${e[m]}: ${h.toFixed(2)}x">
</div>
`})}
</div>
@@ -1693,30 +1743,30 @@ ${p?`<div class="sub">${p}</div>`:""}
<span class="legend-item"><span class="dot high"></span> ${s("longer",d)||"L\xE4nger"}</span>
</div>
</div>
`}function ct(n,d){return Pt(n,d)}function Pt(n,d){let t=n.seasonal_factors??n.interval_analysis?.seasonal_factors;if(!t||t.length!==12)return c;let e=n.interval_analysis?.seasonal_reason,i=new Date().getMonth(),o=300,u=100,_=8,m=u-_-4,y=Math.max(...t,1.5),I=o/12,z=I*.65,w=_+m-1/y*m;return r`
`}function pt(n,d){return Mt(n,d)}function Mt(n,d){let t=n.seasonal_factors??n.interval_analysis?.seasonal_factors;if(!t||t.length!==12)return c;let e=n.interval_analysis?.seasonal_reason,i=new Date().getMonth(),o=300,_=100,h=8,u=_-h-4,y=Math.max(...t,1.5),I=o/12,F=I*.65,w=h+u-1/y*u;return r`
<div class="seasonal-chart">
<div class="seasonal-chart-title">
<ha-svg-icon aria-hidden="true" path="M17.75 4.09L15.22 6.03L16.13 9.09L13.5 7.28L10.87 9.09L11.78 6.03L9.25 4.09L12.44 4L13.5 1L14.56 4L17.75 4.09M21.25 11L19.61 12.25L20.2 14.23L18.5 13.06L16.8 14.23L17.39 12.25L15.75 11L17.81 10.95L18.5 9L19.19 10.95L21.25 11M18.97 15.95C19.8 15.87 20.69 17.05 20.16 17.8C19.84 18.25 19.5 18.67 19.08 19.07C15.17 23 8.84 23 4.94 19.07C1.03 15.17 1.03 8.83 4.94 4.93C5.34 4.53 5.76 4.17 6.21 3.85C6.96 3.32 8.14 4.21 8.06 5.04C7.79 7.9 8.75 10.87 10.95 13.06C13.14 15.26 16.1 16.22 18.97 15.95Z"></ha-svg-icon>
${s("seasonal_chart_title",d)}
${e?r`<span class="source-tag">${e==="learned"?s("seasonal_learned",d):s("seasonal_manual",d)}</span>`:c}
</div>
<svg viewBox="0 0 ${o} ${u}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${s("chart_seasonal",d)}">
<svg viewBox="0 0 ${o} ${_}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${s("chart_seasonal",d)}">
<line x1="0" y1="${w.toFixed(1)}" x2="${o}" y2="${w.toFixed(1)}"
stroke="var(--divider-color)" stroke-width="1" stroke-dasharray="4,3" />
${t.map((H,S)=>{let D=H/y*m,K=S*I+(I-z)/2,q=_+m-D,j=S===i,O=H<1?"var(--success-color, #4caf50)":H>1?"var(--warning-color, #ff9800)":"var(--secondary-text-color)";return M`
<rect x="${K.toFixed(1)}" y="${q.toFixed(1)}"
width="${z.toFixed(1)}" height="${D.toFixed(1)}"
${t.map((H,S)=>{let D=H/y*u,K=S*I+(I-F)/2,z=h+u-D,j=S===i,O=H<1?"var(--success-color, #4caf50)":H>1?"var(--warning-color, #ff9800)":"var(--secondary-text-color)";return q`
<rect x="${K.toFixed(1)}" y="${z.toFixed(1)}"
width="${F.toFixed(1)}" height="${D.toFixed(1)}"
fill="${O}" opacity="${j?1:.5}" rx="2" />
`})}
</svg>
<div class="seasonal-labels">
${lt.map((H,S)=>r`<span class="seasonal-label ${S===i?"active-month":""}">${s(H,d)}</span>`)}
${dt.map((H,S)=>r`<span class="seasonal-label ${S===i?"active-month":""}">${s(H,d)}</span>`)}
</div>
</div>
`}var b=class extends ${constructor(){super(...arguments);this._open=!1;this._entryId=null;this._taskId=null;this._task=null;this._objectName="";this._busy=!1;this._error="";this._showSkip=!1;this._showReset=!1;this._showDetails=!1;this._showAdaptive=!1;this._skipReason="";this._resetDate="";this._features={adaptive:!1,predictions:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1};this._toast="";this._featuresLoaded=!1}get _lang(){return this.hass?.language||"en"}async openFor(t,e){this._entryId=t,this._taskId=e,this._error="",this._showSkip=!1,this._showReset=!1,this._showAdaptive=!1,this._skipReason="",this._resetDate=new Date().toISOString().slice(0,10),this._open=!0,await Promise.all([this._loadTask(),this._loadFeatures()])}async _loadFeatures(){if(!this._featuresLoaded)try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/settings"});t?.features&&(this._features={...this._features,...t.features}),this._featuresLoaded=!0}catch{}}close(){this._open=!1,this._task=null,this._error=""}async _loadTask(){if(!(!this._entryId||!this._taskId))try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this._objectName=t.object?.name||"";let e=(t.tasks||[]).find(i=>i.id===this._taskId);this._task=e??null}catch(t){this._error=k(t,this._lang)}}async _runWs(t){this._busy=!0,this._error="";try{return await this.hass.connection.sendMessagePromise(t),this._busy=!1,!0}catch(e){return this._error=k(e,this._lang),this._busy=!1,!1}}_notifyChanged(t){this.dispatchEvent(new CustomEvent("task-action-fired",{detail:{entry_id:this._entryId,task_id:this._taskId,action:t},bubbles:!0,composed:!0}))}_onComplete(){!this._entryId||!this._taskId||!this._task||import("./dialog-mount-RTTXPFO3.js").then(({openCompleteDialog:t})=>{t({entry_id:this._entryId,task_id:this._taskId,task_name:this._task.name,checklist:this._task.checklist||[],adaptive_enabled:!!this._task.adaptive_config?.enabled})&&(this._notifyChanged("complete"),this.close())})}async _onSkipConfirm(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/skip",entry_id:this._entryId,task_id:this._taskId,reason:this._skipReason.trim()||null})&&(this._notifyChanged("skip"),this.close())}async _onResetConfirm(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/reset",entry_id:this._entryId,task_id:this._taskId,date:this._resetDate||void 0})&&(this._notifyChanged("reset"),this.close())}_onEdit(){!this._entryId||!this._taskId||import("./dialog-mount-RTTXPFO3.js").then(({openEditTaskDialog:t})=>{t(this._entryId,this._taskId),this.close()})}_onQr(){!this._entryId||!this._taskId||!this._task||import("./dialog-mount-RTTXPFO3.js").then(({openQrDialog:t})=>{t({entry_id:this._entryId,task_id:this._taskId,task_name:this._task.name,object_name:this._objectName}),this.close()})}async _onDelete(){if(!this._entryId||!this._taskId)return;let t=s("delete_task_confirm",this._lang)||`Delete "${this._task?.name}"?`;if(!window.confirm(t))return;await this._runWs({type:"maintenance_supporter/task/delete",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("delete"),this.close())}async _onArchive(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/archive",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("archive"),this.close())}async _onUnarchive(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/unarchive",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("unarchive"),this.close())}_onOpenInPanel(){if(!this._entryId||!this._taskId)return;let t=`/maintenance-supporter?entry_id=${encodeURIComponent(this._entryId)}&task_id=${encodeURIComponent(this._taskId)}`;history.pushState(null,"",t),window.dispatchEvent(new CustomEvent("location-changed")),this.close()}async _applySuggestion(){if(!this._entryId||!this._taskId||!this._task?.suggested_interval)return;await this._runWs({type:"maintenance_supporter/task/apply_suggestion",entry_id:this._entryId,task_id:this._taskId,interval:this._task.suggested_interval})&&(this._toast=s("suggestion_applied",this._lang)||"Applied",this._notifyChanged("apply_suggestion"),await this._loadTask(),setTimeout(()=>{this._toast=""},2500))}async _reanalyzeInterval(){if(!(!this._entryId||!this._taskId)){this._busy=!0,this._error="";try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/analyze_interval",entry_id:this._entryId,task_id:this._taskId});this._toast=t.recommended_interval?`${s("reanalyze_result",this._lang)||"Recomputed"}: ${t.recommended_interval}d (${t.data_points} pts)`:s("reanalyze_insufficient_data",this._lang)||"Not enough data",await this._loadTask(),setTimeout(()=>{this._toast=""},3500)}catch(t){this._error=k(t,this._lang)}finally{this._busy=!1}}}_onEditHistoryEntry( Line truncated
`}var b=class extends x{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._taskId=null;this._task=null;this._objectName="";this._busy=!1;this._error="";this._showSkip=!1;this._showReset=!1;this._showDetails=!1;this._showAdaptive=!1;this._skipReason="";this._resetDate="";this._features={adaptive:!1,predictions:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1};this._toast="";this._featuresLoaded=!1}get _lang(){return this.hass?.language||"en"}async openFor(t,e){this._entryId=t,this._taskId=e,this._error="",this._showSkip=!1,this._showReset=!1,this._showAdaptive=!1,this._skipReason="",this._resetDate=new Date().toISOString().slice(0,10),this._open=!0,await Promise.all([this._loadTask(),this._loadFeatures()])}async _loadFeatures(){if(!this._featuresLoaded)try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/settings"});t?.features&&(this._features={...this._features,...t.features}),this._featuresLoaded=!0}catch{}}close(){this._open=!1,this._task=null,this._error=""}async _loadTask(){if(!(!this._entryId||!this._taskId))try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this._objectName=t.object?.name||"";let e=(t.tasks||[]).find(i=>i.id===this._taskId);this._task=e??null}catch(t){this._error=k(t,this._lang)}}async _runWs(t){this._busy=!0,this._error="";try{return await this.hass.connection.sendMessagePromise(t),this._busy=!1,!0}catch(e){return this._error=k(e,this._lang),this._busy=!1,!1}}_notifyChanged(t){this.dispatchEvent(new CustomEvent("task-action-fired",{detail:{entry_id:this._entryId,task_id:this._taskId,action:t},bubbles:!0,composed:!0}))}_onComplete(){!this._entryId||!this._taskId||!this._task||import("./dialog-mount-3CFCNZ5R.js").then(({openCompleteDialog:t})=>{t({entry_id:this._entryId,task_id:this._taskId,task_name:this._task.name,checklist:this._task.checklist||[],adaptive_enabled:!!this._task.adaptive_config?.enabled})&&(this._notifyChanged("complete"),this.close())})}async _onSkipConfirm(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/skip",entry_id:this._entryId,task_id:this._taskId,reason:this._skipReason.trim()||null})&&(this._notifyChanged("skip"),this.close())}async _onResetConfirm(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/reset",entry_id:this._entryId,task_id:this._taskId,date:this._resetDate||void 0})&&(this._notifyChanged("reset"),this.close())}_onEdit(){!this._entryId||!this._taskId||import("./dialog-mount-3CFCNZ5R.js").then(({openEditTaskDialog:t})=>{t(this._entryId,this._taskId),this.close()})}_onQr(){!this._entryId||!this._taskId||!this._task||import("./dialog-mount-3CFCNZ5R.js").then(({openQrDialog:t})=>{t({entry_id:this._entryId,task_id:this._taskId,task_name:this._task.name,object_name:this._objectName}),this.close()})}async _onDelete(){if(!this._entryId||!this._taskId)return;let t=s("delete_task_confirm",this._lang)||`Delete "${this._task?.name}"?`;if(!window.confirm(t))return;await this._runWs({type:"maintenance_supporter/task/delete",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("delete"),this.close())}async _onArchive(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/archive",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("archive"),this.close())}async _onUnarchive(){if(!this._entryId||!this._taskId)return;await this._runWs({type:"maintenance_supporter/task/unarchive",entry_id:this._entryId,task_id:this._taskId})&&(this._notifyChanged("unarchive"),this.close())}_onOpenInPanel(){if(!this._entryId||!this._taskId)return;let t=`/maintenance-supporter?entry_id=${encodeURIComponent(this._entryId)}&task_id=${encodeURIComponent(this._taskId)}`;history.pushState(null,"",t),window.dispatchEvent(new CustomEvent("location-changed")),this.close()}async _applySuggestion(){if(!this._entryId||!this._taskId||!this._task?.suggested_interval)return;await this._runWs({type:"maintenance_supporter/task/apply_suggestion",entry_id:this._entryId,task_id:this._taskId,interval:this._task.suggested_interval})&&(this._toast=s("suggestion_applied",this._lang)||"Applied",this._notifyChanged("apply_suggestion"),await this._loadTask(),setTimeout(()=>{this._toast=""},2500))}async _reanalyzeInterval(){if(!(!this._entryId||!this._taskId)){this._busy=!0,this._error="";try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/analyze_interval",entry_id:this._entryId,task_id:this._taskId});this._toast=t.recommended_interval?`${s("reanalyze_result",this._lang)||"Recomputed"}: ${t.recommended_interval}d (${t.data_points} pts)`:s("reanalyze_insufficient_data",this._lang)||"Not enough data",await this._loadTask(),setTimeout(()=>{this._toast=""},3500)}catch(t){this._error=k(t,this._lang)}finally{this._busy=!1}}}_onEditHistoryEntry( Line truncated
<div class="recommendation-card">
<h4>${s("suggested_interval",e)}</h4>
${ot(t.interval_days,t.suggested_interval,t.interval_confidence||"medium",e)}
${lt(t.interval_days,t.suggested_interval,t.interval_confidence||"medium",e)}
<div class="recommendation-actions">
<button class="btn primary"
@click=${this._applySuggestion} ?disabled=${this._busy}>
@@ -1730,20 +1780,20 @@ ${p?`<div class="sub">${p}</div>`:""}
</button>
</div>
</div>
`}_renderAdaptive(t){let e=this._lang,i=this._features.adaptive&&t.suggested_interval&&t.suggested_interval!==t.interval_days,o=t.degradation_trend!=null&&t.degradation_trend!=="insufficient_data"||t.days_until_threshold!=null||t.environmental_factor!=null&&t.environmental_factor!==1,u=this._features.adaptive&&t.interval_analysis?.weibull_beta!=null&&t.interval_analysis?.weibull_eta!=null,_=this._features.seasonal&&t.seasonal_factor&&t.seasonal_factor!==1;return!i&&!o&&!u&&!_?r`<div class="adaptive-empty">
`}_renderAdaptive(t){let e=this._lang,i=this._features.adaptive&&t.suggested_interval&&t.suggested_interval!==t.interval_days,o=t.degradation_trend!=null&&t.degradation_trend!=="insufficient_data"||t.days_until_threshold!=null||t.environmental_factor!=null&&t.environmental_factor!==1,_=this._features.adaptive&&t.interval_analysis?.weibull_beta!=null&&t.interval_analysis?.weibull_eta!=null,h=this._features.seasonal&&t.seasonal_factor&&t.seasonal_factor!==1;return!i&&!o&&!_&&!h?r`<div class="adaptive-empty">
${s("adaptive_no_data",e)||"Not enough completion history yet for adaptive analysis."}
</div>`:r`
<div class="adaptive-stack">
${this._toast?r`<div class="toast">${this._toast}</div>`:c}
${i?this._renderRecommendation(t):c}
${o?nt(t,e,this._features):c}
${u?rt(t,e):c}
${_?r`
${dt(t,e,this._features)}
${t.seasonal_factors?.length===12||t.interval_analysis?.seasonal_factors?.length===12?ct(t,e):c}
${o?ot(t,e,this._features):c}
${_?nt(t,e):c}
${h?r`
${ct(t,e,this._features)}
${t.seasonal_factors?.length===12||t.interval_analysis?.seasonal_factors?.length===12?pt(t,e):c}
`:c}
</div>
`}_renderDetails(t){let e=this._lang,i=t.history||[],o=i.filter(p=>p.type==="completed"),u=o.reduce((p,m)=>p+(typeof m.cost=="number"?m.cost:0),0),_=(()=>{let p=o.map(m=>typeof m.duration=="number"?m.duration:null).filter(m=>m!=null);return p.length?Math.round(p.reduce((m,y)=>m+y,0)/p.length):null})();return r`
`}_renderDetails(t){let e=this._lang,i=t.history||[],o=i.filter(m=>m.type==="completed"),_=o.reduce((m,u)=>m+(typeof u.cost=="number"?u.cost:0),0),h=(()=>{let m=o.map(u=>typeof u.duration=="number"?u.duration:null).filter(u=>u!=null);return m.length?Math.round(m.reduce((u,y)=>u+y,0)/m.length):null})();return r`
<div class="details">
<div class="stats-grid">
<div class="stat">
@@ -1752,11 +1802,11 @@ ${p?`<div class="sub">${p}</div>`:""}
</div>
<div class="stat">
<span class="stat-label">${s("total_cost",e)||"Total cost"}</span>
<span class="stat-value">${u.toFixed(2)}</span>
<span class="stat-value">${_.toFixed(2)}</span>
</div>
<div class="stat">
<span class="stat-label">${s("avg_duration",e)||"Avg duration"}</span>
<span class="stat-value">${_!=null?`${_}m`:"\u2014"}</span>
<span class="stat-value">${h!=null?`${h}m`:"\u2014"}</span>
</div>
</div>
<div class="history-header">
@@ -1765,21 +1815,21 @@ ${p?`<div class="sub">${p}</div>`:""}
</div>
${i.length===0?r`<div class="history-empty">${s("history_empty",e)||"No history yet."}</div>`:r`
<div class="history-list">
${[...i].reverse().slice(0,20).map(p=>{let m=["completed","reset","skipped"].includes(p.type);return r`
${[...i].reverse().slice(0,20).map(m=>{let u=["completed","reset","skipped"].includes(m.type);return r`
<div class="history-entry">
<div class="history-line">
<span class="history-type type-${p.type}">${s(p.type,e)}</span>
<span class="history-date">${X(p.timestamp,e)}</span>
${m?r`<button class="history-edit"
<span class="history-type type-${m.type}">${s(m.type,e)}</span>
<span class="history-date">${X(m.timestamp,e)}</span>
${u?r`<button class="history-edit"
title="${s("history_edit_button",e)||"Edit"}"
@click=${()=>this._onEditHistoryEntry(p)}>
@click=${()=>this._onEditHistoryEntry(m)}>
<ha-icon icon="mdi:pencil"></ha-icon>
</button>`:c}
</div>
${p.notes?r`<div class="history-notes">${p.notes}</div>`:c}
${p.cost!=null||p.duration!=null?r`<div class="history-meta">
${p.cost!=null?r`<span>💰 ${p.cost.toFixed(2)}</span>`:c}
${p.duration!=null?r`<span>⏱️ ${p.duration}m</span>`:c}
${m.notes?r`<div class="history-notes">${m.notes}</div>`:c}
${m.cost!=null||m.duration!=null?r`<div class="history-meta">
${m.cost!=null?r`<span>💰 ${m.cost.toFixed(2)}</span>`:c}
${m.duration!=null?r`<span>⏱️ ${m.duration}m</span>`:c}
</div>`:c}
</div>
`})}
@@ -1797,11 +1847,11 @@ ${p?`<div class="sub">${p}</div>`:""}
<span class="task-name">${e.name}</span>
</div>
<div class="object">
<button class="link-inline" @click=${()=>{this._entryId&&import("./dialog-mount-RTTXPFO3.js").then(({openObjectQuickActions:o})=>{o(this._entryId),this.close()})}}>${this._objectName}</button>
<button class="link-inline" @click=${()=>{this._entryId&&import("./dialog-mount-3CFCNZ5R.js").then(({openObjectQuickActions:o})=>{o(this._entryId),this.close()})}}>${this._objectName}</button>
</div>
<div class="quick-info">
${e.next_due?r`<span><strong>${s("next_due",t)||"Next due"}:</strong> ${U(e.next_due,t)}</span>`:c}
${e.last_performed?r`<span><strong>${s("last_performed",t)||"Last"}:</strong> ${U(e.last_performed,t)}</span>`:c}
${e.next_due?r`<span><strong>${s("next_due",t)||"Next due"}:</strong> ${N(e.next_due,t)}</span>`:c}
${e.last_performed?r`<span><strong>${s("last_performed",t)||"Last"}:</strong> ${N(e.last_performed,t)}</span>`:c}
${e.schedule?.kind&&!["manual","one_time"].includes(e.schedule.kind)||e.interval_days!=null?r`<span><strong>${s("interval",t)||"Interval"}:</strong> ${tt(e,t)}</span>`:c}
</div>
</div>
@@ -1894,7 +1944,7 @@ ${p?`<div class="sub">${p}</div>`:""}
`}
`:r`<div class="loading">${s("loading",t)||"Loading\u2026"}</div>`}
</div>
`}};b.styles=[it,x`
`}};b.styles=[it,$`
:host { display: contents; }
.backdrop {
position: fixed; inset: 0; z-index: 100;
@@ -2074,7 +2124,7 @@ ${p?`<div class="sub">${p}</div>`:""}
.weibull-section, .seasonal-card-compact { max-width: 100%; }
.weibull-chart svg { max-width: 100%; height: auto; }
.details-toggle { gap: 12px; flex-wrap: wrap; }
`],a([g({attribute:!1})],b.prototype,"hass",2),a([l()],b.prototype,"_open",2),a([l()],b.prototype,"_entryId",2),a([l()],b.prototype,"_taskId",2),a([l()],b.prototype,"_task",2),a([l()],b.prototype,"_objectName",2),a([l()],b.prototype,"_busy",2),a([l()],b.prototype,"_error",2),a([l()],b.prototype,"_showSkip",2),a([l()],b.prototype,"_showReset",2),a([l()],b.prototype,"_showDetails",2),a([l()],b.prototype,"_showAdaptive",2),a([l()],b.prototype,"_skipReason",2),a([l()],b.prototype,"_resetDate",2),a([l()],b.prototype,"_features",2),a([l()],b.prototype,"_toast",2);customElements.get("maintenance-task-quick-actions-dialog")||customElements.define("maintenance-task-quick-actions-dialog",b);var C=class extends ${constructor(){super(...arguments);this._open=!1;this._entryId=null;this._data=null;this._busy=!1;this._error=""}get _lang(){return this.hass?.language||"en"}async openFor(t){this._entryId=t,this._error="",this._open=!0,await this._load()}close(){this._open=!1,this._data=null,this._error=""}async _load(){if(this._entryId)try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this._data=t}catch(t){this._error=k(t,this._lang)}}_onEditObject(){!this._entryId||!this._data||import("./dialog-mount-RTTXPFO3.js").then(({openEditObjectDialog:t})=>{t(this._entryId,this._data.object),this.close()})}_onAddTask(){this._entryId&&import("./dialog-mount-RTTXPFO3.js").then(({openCreateTaskDialog:t})=>{t(),this.close()})}async _onDelete(){if(!this._entryId||!this._data)return;let t=s("delete_object_confirm",this._lang)||`Delete "${this._data.object.name}" and all its tasks?`;if(window.confirm(t)){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/delete",entry_id:this._entryId}),this.dispatchEvent(new CustomEvent("object-deleted",{detail:{entry_id:this._entryId},bubbles:!0,composed:!0})),this.close()}catch(e){this._error=k(e,this._lang)}finally{this._busy=!1}}}async _onArchiveObject(){if(!this._entryId||!this._data)return;let t=!!this._data.object.archived;if(!t){let e=s("confirm_archive_object",this._lang)||"Archive this object and its tasks?";if(!window.confirm(e))return}this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:t?"maintenance_supporter/object/unarchive":"maintenance_supporter/object/archive",entry_id:this._entryId}),this.dispatchEvent(new CustomEvent("object-changed",{detail:{entry_id:this._entryId},bubbles:!0,composed:!0})),this.close()}catch(e){this._error=k(e,this._lang)}finally{this._busy=!1}}_onTaskClick(t){this._entryId&&import("./dialog-mount-RTTXPFO3.js").then(({openTaskQuickActions:e})=>{e(this._entryId,t)})}render(){if(!this._open)return c;let t=this._lang,e=this._data,i=e?.object,o=e?.tasks||[],u=this.hass?.user?.is_admin??!0;return r`
`],a([g({attribute:!1})],b.prototype,"hass",2),a([l()],b.prototype,"_open",2),a([l()],b.prototype,"_entryId",2),a([l()],b.prototype,"_taskId",2),a([l()],b.prototype,"_task",2),a([l()],b.prototype,"_objectName",2),a([l()],b.prototype,"_busy",2),a([l()],b.prototype,"_error",2),a([l()],b.prototype,"_showSkip",2),a([l()],b.prototype,"_showReset",2),a([l()],b.prototype,"_showDetails",2),a([l()],b.prototype,"_showAdaptive",2),a([l()],b.prototype,"_skipReason",2),a([l()],b.prototype,"_resetDate",2),a([l()],b.prototype,"_features",2),a([l()],b.prototype,"_toast",2);customElements.get("maintenance-task-quick-actions-dialog")||customElements.define("maintenance-task-quick-actions-dialog",b);var C=class extends x{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._data=null;this._busy=!1;this._error=""}get _lang(){return this.hass?.language||"en"}async openFor(t){this._entryId=t,this._error="",this._open=!0,await this._load()}close(){this._open=!1,this._data=null,this._error=""}async _load(){if(this._entryId)try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId});this._data=t}catch(t){this._error=k(t,this._lang)}}_onEditObject(){!this._entryId||!this._data||import("./dialog-mount-3CFCNZ5R.js").then(({openEditObjectDialog:t})=>{t(this._entryId,this._data.object),this.close()})}_onAddTask(){this._entryId&&import("./dialog-mount-3CFCNZ5R.js").then(({openCreateTaskDialog:t})=>{t(),this.close()})}async _onDelete(){if(!this._entryId||!this._data)return;let t=s("delete_object_confirm",this._lang)||`Delete "${this._data.object.name}" and all its tasks?`;if(window.confirm(t)){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/delete",entry_id:this._entryId}),this.dispatchEvent(new CustomEvent("object-deleted",{detail:{entry_id:this._entryId},bubbles:!0,composed:!0})),this.close()}catch(e){this._error=k(e,this._lang)}finally{this._busy=!1}}}async _onArchiveObject(){if(!this._entryId||!this._data)return;let t=!!this._data.object.archived;if(!t){let e=s("confirm_archive_object",this._lang)||"Archive this object and its tasks?";if(!window.confirm(e))return}this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:t?"maintenance_supporter/object/unarchive":"maintenance_supporter/object/archive",entry_id:this._entryId}),this.dispatchEvent(new CustomEvent("object-changed",{detail:{entry_id:this._entryId},bubbles:!0,composed:!0})),this.close()}catch(e){this._error=k(e,this._lang)}finally{this._busy=!1}}_onTaskClick(t){this._entryId&&import("./dialog-mount-3CFCNZ5R.js").then(({openTaskQuickActions:e})=>{e(this._entryId,t)})}render(){if(!this._open)return c;let t=this._lang,e=this._data,i=e?.object,o=e?.tasks||[],_=this.hass?.user?.is_admin??!0;return r`
<div class="backdrop" @click=${this.close}></div>
<div class="dialog" role="dialog" aria-modal="true">
${e&&i?r`
@@ -2092,11 +2142,11 @@ ${p?`<div class="sub">${p}</div>`:""}
</div>
${o.length===0?r`<div class="empty">${s("no_tasks",t)||"No tasks yet."}</div>`:r`
<div class="task-list">
${o.map(_=>r`
<div class="task-row" @click=${()=>this._onTaskClick(_.id)}>
<span class="status-dot" style="background: ${V[_.status]||"#ccc"}"></span>
<span class="task-name">${_.name}</span>
<span class="task-status">${s(_.status||"ok",t)}</span>
${o.map(h=>r`
<div class="task-row" @click=${()=>this._onTaskClick(h.id)}>
<span class="status-dot" style="background: ${V[h.status]||"#ccc"}"></span>
<span class="task-name">${h.name}</span>
<span class="task-status">${s(h.status||"ok",t)}</span>
</div>
`)}
</div>
@@ -2110,7 +2160,7 @@ ${p?`<div class="sub">${p}</div>`:""}
</div>
`:c}
${u?r`
${_?r`
<div class="actions">
<button class="btn primary" @click=${this._onAddTask} ?disabled=${this._busy}>
<ha-icon icon="mdi:plus"></ha-icon>
@@ -2134,14 +2184,14 @@ ${p?`<div class="sub">${p}</div>`:""}
</div>
`}_renderMetaRow(t){let e=this._lang,i=[];return t.area_id&&i.push([s("area",e),t.area_id]),t.manufacturer&&i.push([s("manufacturer",e),t.manufacturer]),t.model&&i.push([s("model",e),t.model]),t.serial_number&&i.push([s("serial_number_label",e),t.serial_number]),t.installation_date&&i.push([s("installed",e),t.installation_date]),t.warranty_expiry&&i.push([s("warranty",e),t.warranty_expiry]),t.documentation_url&&i.push([s("documentation_url_label",e),t.documentation_url]),i.length===0?c:r`
<div class="meta">
${i.map(([o,u])=>r`
${i.map(([o,_])=>r`
<div class="meta-item">
<span class="meta-label">${o}</span>
<span class="meta-value">${/^https?:\/\//i.test(u)?r`<a href="${u}" target="_blank" rel="noopener noreferrer">${u}</a>`:u}</span>
<span class="meta-value">${/^https?:\/\//i.test(_)?r`<a href="${_}" target="_blank" rel="noopener noreferrer">${_}</a>`:_}</span>
</div>
`)}
</div>
`}};C.styles=x`
`}};C.styles=$`
:host { display: contents; }
.backdrop {
position: fixed; inset: 0; z-index: 100; background: rgba(0,0,0,0.5);
@@ -2200,4 +2250,4 @@ ${p?`<div class="sub">${p}</div>`:""}
.btn ha-icon { --mdc-icon-size: 16px; }
.loading { padding: 24px; text-align: center; color: var(--secondary-text-color); }
.error { padding: 8px; border-radius: 6px; background: rgba(211,47,47,0.1); color: var(--error-color); font-size: 13px; }
`,a([g({attribute:!1})],C.prototype,"hass",2),a([l()],C.prototype,"_open",2),a([l()],C.prototype,"_entryId",2),a([l()],C.prototype,"_data",2),a([l()],C.prototype,"_busy",2),a([l()],C.prototype,"_error",2);customElements.get("maintenance-object-quick-actions-dialog")||customElements.define("maintenance-object-quick-actions-dialog",C);var ht="maintenance-object-dialog",_t="maintenance-task-dialog",Ft="maintenance-history-edit-dialog",zt="maintenance-complete-dialog",qt="maintenance-qr-dialog",Mt="maintenance-task-quick-actions-dialog",Nt="maintenance-object-quick-actions-dialog";function G(){return document.querySelector("home-assistant")?.hass}function P(n){let d=document.body.querySelector(n);return d||(d=document.createElement(n),document.body.appendChild(d)),d}function F(n){let d=G();if(!d)return!1;n.hass=d;let t=d.language||"en";return Z(t)||J(t).then(()=>{n.requestUpdate?.()}),!0}var pt={features:{adaptive:!1,predictions:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1},defaultWarningDays:7},B=null;function ut(n){return B||(B=n.connection.sendMessagePromise({type:"maintenance_supporter/settings"}).then(d=>({features:d.features??pt.features,defaultWarningDays:d.general?.default_warning_days??7})).catch(()=>pt),B)}function ri(){let n=P(ht);return F(n)?(n.openCreate(),!0):!1}function ni(n,d){let t=P(ht);return F(t)?(t.openEdit(n,d),!0):!1}function oi(){let n=P(_t);if(!F(n))return!1;let d=G();return d?((async()=>{let t=await ut(d),e=n;e.checklistsEnabled=t.features.checklists,e.scheduleTimeEnabled=t.features.schedule_time,e.completionActionsEnabled=t.features.completion_actions,e.defaultWarningDays=t.defaultWarningDays,e.openCreate()})(),!0):!1}function li(n,d){let t=P(_t);if(!F(t))return!1;let e=G();return e?((async()=>{try{let[i,o]=await Promise.all([e.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:n}),ut(e)]),u=(i.tasks||[]).find(p=>p.id===d);if(!u){console.warn(`openEditTaskDialog: task ${d} not found in entry ${n}`);return}let _=t;_.checklistsEnabled=o.features.checklists,_.scheduleTimeEnabled=o.features.schedule_time,_.completionActionsEnabled=o.features.completion_actions,_.defaultWarningDays=o.defaultWarningDays,await _.openEdit(n,u)}catch(i){console.warn("openEditTaskDialog: failed to load task/features",i)}})(),!0):!1}function di(n){let d=P(Ft);return F(d)?(d.openEdit(n),!0):!1}function ci(n){let d=P(zt);return F(d)?(d.entryId=n.entry_id,d.taskId=n.task_id,d.taskName=n.task_name,d.checklist=n.checklist??[],d.adaptiveEnabled=!!n.adaptive_enabled,d.lang=G()?.language||"en",d.open(),!0):!1}function pi(n){let d=P(qt);return F(d)?(d.openForTask(n.entry_id,n.task_id,n.object_name,n.task_name),!0):!1}function hi(n,d){let t=P(Mt);return F(t)?(t.openFor(n,d),!0):!1}function _i(n){let d=P(Nt);return F(d)?(d.openFor(n),!0):!1}export{ci as openCompleteDialog,ri as openCreateObjectDialog,oi as openCreateTaskDialog,ni as openEditObjectDialog,li as openEditTaskDialog,di as openHistoryEditDialog,_i as openObjectQuickActions,pi as openQrDialog,hi as openTaskQuickActions};
`,a([g({attribute:!1})],C.prototype,"hass",2),a([l()],C.prototype,"_open",2),a([l()],C.prototype,"_entryId",2),a([l()],C.prototype,"_data",2),a([l()],C.prototype,"_busy",2),a([l()],C.prototype,"_error",2);customElements.get("maintenance-object-quick-actions-dialog")||customElements.define("maintenance-object-quick-actions-dialog",C);var _t="maintenance-object-dialog",ut="maintenance-task-dialog",Ft="maintenance-history-edit-dialog",zt="maintenance-complete-dialog",qt="maintenance-qr-dialog",Ut="maintenance-task-quick-actions-dialog",Nt="maintenance-object-quick-actions-dialog";function G(){return document.querySelector("home-assistant")?.hass}function P(n){let d=document.body.querySelector(n);return d||(d=document.createElement(n),document.body.appendChild(d)),d}function M(n){let d=G();if(!d)return!1;n.hass=d;let t=d.language||"en";return Z(t)||J(t).then(()=>{n.requestUpdate?.()}),!0}var ht={features:{adaptive:!1,predictions:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1},defaultWarningDays:7},B=null;function mt(n){return B||(B=n.connection.sendMessagePromise({type:"maintenance_supporter/settings"}).then(d=>({features:d.features??ht.features,defaultWarningDays:d.general?.default_warning_days??7})).catch(()=>ht),B)}function ni(){let n=P(_t);return M(n)?(n.openCreate(),!0):!1}function oi(n,d){let t=P(_t);return M(t)?(t.openEdit(n,d),!0):!1}function li(){let n=P(ut);if(!M(n))return!1;let d=G();return d?((async()=>{let t=await mt(d),e=n;e.checklistsEnabled=t.features.checklists,e.scheduleTimeEnabled=t.features.schedule_time,e.completionActionsEnabled=t.features.completion_actions,e.defaultWarningDays=t.defaultWarningDays,e.openCreate()})(),!0):!1}function di(n,d){let t=P(ut);if(!M(t))return!1;let e=G();return e?((async()=>{try{let[i,o]=await Promise.all([e.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:n}),mt(e)]),_=(i.tasks||[]).find(m=>m.id===d);if(!_){console.warn(`openEditTaskDialog: task ${d} not found in entry ${n}`);return}let h=t;h.checklistsEnabled=o.features.checklists,h.scheduleTimeEnabled=o.features.schedule_time,h.completionActionsEnabled=o.features.completion_actions,h.defaultWarningDays=o.defaultWarningDays,await h.openEdit(n,_)}catch(i){console.warn("openEditTaskDialog: failed to load task/features",i)}})(),!0):!1}function ci(n){let d=P(Ft);return M(d)?(d.openEdit(n),!0):!1}function pi(n){let d=P(zt);return M(d)?(d.entryId=n.entry_id,d.taskId=n.task_id,d.taskName=n.task_name,d.checklist=n.checklist??[],d.adaptiveEnabled=!!n.adaptive_enabled,d.lang=G()?.language||"en",d.open(),!0):!1}function hi(n){let d=P(qt);return M(d)?(d.openForTask(n.entry_id,n.task_id,n.object_name,n.task_name),!0):!1}function _i(n,d){let t=P(Ut);return M(t)?(t.openFor(n,d),!0):!1}function ui(n){let d=P(Nt);return M(d)?(d.openFor(n),!0):!1}export{pi as openCompleteDialog,ni as openCreateObjectDialog,li as openCreateTaskDialog,oi as openEditObjectDialog,di as openEditTaskDialog,ci as openHistoryEditDialog,ui as openObjectQuickActions,hi as openQrDialog,_i as openTaskQuickActions};
@@ -1,4 +1,4 @@
import{a as m}from"./chunk-6EEEWEML.js";import{a as u,b as s,d as l,e as h,f as g,g as o,j as i,l as _,s as p}from"./chunk-2LLDFX26.js";import{a}from"./chunk-LO2NM3CE.js";var e=class extends h{constructor(){super(...arguments);this._config={type:""};this._groups={};this._loaded=!1;this._busy=!1;this._error="";this._newName="";this._editingId=null;this._editingName="";this._hasInitiallyLoaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return this.hass?.language||"en"}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._hasInitiallyLoaded&&(this._hasInitiallyLoaded=!0,this._load(),_(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/groups"});this._groups=t.groups||{},this._loaded=!0}catch(t){this._error=p(t,this._lang)}}async _addGroup(){if(!this._isAdmin)return;let t=this._newName.trim();if(t){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/create",name:t}),this._newName="",await this._load()}catch(r){this._error=p(r,this._lang)}finally{this._busy=!1}}}_startEdit(t){this._editingId=t,this._editingName=this._groups[t]?.name||""}async _saveEdit(){if(!this._isAdmin||!this._editingId)return;let t=this._editingName.trim();if(t){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/update",group_id:this._editingId,name:t}),this._editingId=null,this._editingName="",await this._load()}catch(r){this._error=p(r,this._lang)}finally{this._busy=!1}}}async _deleteGroup(t,r){if(!this._isAdmin)return;let n=(i("group_delete_confirm",this._lang)||'Delete group "{name}"?').replace("{name}",r);if(window.confirm(n)){this._busy=!0;try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/delete",group_id:t}),await this._load()}catch(d){this._error=p(d,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_groups"),window.dispatchEvent(new CustomEvent("location-changed"))}_onKeyDown(t,r){t.key==="Enter"?(t.preventDefault(),r()):t.key==="Escape"&&(t.preventDefault(),this._editingId=null,this._editingName="")}render(){let t=this._lang;if(!this._loaded)return s`<ha-card><div class="loading">${i("loading",t)||"Loading\u2026"}</div></ha-card>`;let r=Object.keys(this._groups);return s`
import{a as m}from"./chunk-SHU63LHK.js";import{a as u,b as s,d as l,e as h,f as g,g as o,j as i,l as _,s as p}from"./chunk-GKOWV2CF.js";import{a}from"./chunk-LO2NM3CE.js";var e=class extends h{constructor(){super(...arguments);this._config={type:""};this._groups={};this._loaded=!1;this._busy=!1;this._error="";this._newName="";this._editingId=null;this._editingName="";this._hasInitiallyLoaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return this.hass?.language||"en"}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._hasInitiallyLoaded&&(this._hasInitiallyLoaded=!0,this._load(),_(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/groups"});this._groups=t.groups||{},this._loaded=!0}catch(t){this._error=p(t,this._lang)}}async _addGroup(){if(!this._isAdmin)return;let t=this._newName.trim();if(t){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/create",name:t}),this._newName="",await this._load()}catch(r){this._error=p(r,this._lang)}finally{this._busy=!1}}}_startEdit(t){this._editingId=t,this._editingName=this._groups[t]?.name||""}async _saveEdit(){if(!this._isAdmin||!this._editingId)return;let t=this._editingName.trim();if(t){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/update",group_id:this._editingId,name:t}),this._editingId=null,this._editingName="",await this._load()}catch(r){this._error=p(r,this._lang)}finally{this._busy=!1}}}async _deleteGroup(t,r){if(!this._isAdmin)return;let n=(i("group_delete_confirm",this._lang)||'Delete group "{name}"?').replace("{name}",r);if(window.confirm(n)){this._busy=!0;try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/delete",group_id:t}),await this._load()}catch(d){this._error=p(d,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_groups"),window.dispatchEvent(new CustomEvent("location-changed"))}_onKeyDown(t,r){t.key==="Enter"?(t.preventDefault(),r()):t.key==="Escape"&&(t.preventDefault(),this._editingId=null,this._editingName="")}render(){let t=this._lang;if(!this._loaded)return s`<ha-card><div class="loading">${i("loading",t)||"Loading\u2026"}</div></ha-card>`;let r=Object.keys(this._groups);return s`
<ha-card>
<div class="card-content">
<div class="header">
@@ -1,4 +1,4 @@
import{a as m}from"./chunk-6EEEWEML.js";import{a as h,b as n,d as c,e as _,f as v,g as r,j as e,l as f,s as l}from"./chunk-2LLDFX26.js";import{a as i}from"./chunk-LO2NM3CE.js";var a=class extends _{constructor(){super(...arguments);this._config={type:""};this._state=null;this._busy=!1;this._error="";this._localStart="";this._localEnd="";this._localBuffer=7;this._dirty=!1;this._loaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return this.hass?.language||"en"}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._loaded&&(this._loaded=!0,this._load(),f(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/state"});this._state=t,this._localStart=t.start||"",this._localEnd=t.end||"",this._localBuffer=t.buffer_days??7,this._dirty=!1}catch(t){this._error=l(t,this._lang)}}async _toggleEnabled(t){this._busy=!0,this._error="";try{let s=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/update",enabled:t});this._state=s}catch(s){this._error=l(s,this._lang)}finally{this._busy=!1}}async _save(){if(this._isAdmin){this._busy=!0,this._error="";try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/update",start:this._localStart||null,end:this._localEnd||null,buffer_days:this._localBuffer});this._state=t,this._dirty=!1}catch(t){this._error=l(t,this._lang)}finally{this._busy=!1}}}async _endNow(){if(this._isAdmin&&window.confirm(e("vacation_end_now_confirm",this._lang)||"End vacation immediately?")){this._busy=!0;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/end_now"});this._state=t,this._localStart=t.start||"",this._localEnd=t.end||""}catch(t){this._error=l(t,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_vacation"),window.dispatchEvent(new CustomEvent("location-changed"))}render(){let t=this._lang,s=this._state;if(!s)return n`<ha-card><div class="loading">${e("loading",t)||"Loading\u2026"}</div></ha-card>`;let p=s.is_active===!0,d=s.enabled===!0,u=s.exempt_task_ids?.length??0,b=p?e("vacation_status_active",t)||"Active now":d?e("vacation_status_scheduled",t)||"Scheduled":e("vacation_status_inactive",t)||"Inactive",g=p?"active":d?"scheduled":"inactive";return n`
import{a as m}from"./chunk-SHU63LHK.js";import{a as h,b as n,d as c,e as _,f as v,g as r,j as e,l as f,s as l}from"./chunk-GKOWV2CF.js";import{a as i}from"./chunk-LO2NM3CE.js";var a=class extends _{constructor(){super(...arguments);this._config={type:""};this._state=null;this._busy=!1;this._error="";this._localStart="";this._localEnd="";this._localBuffer=7;this._dirty=!1;this._loaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return this.hass?.language||"en"}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._loaded&&(this._loaded=!0,this._load(),f(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/state"});this._state=t,this._localStart=t.start||"",this._localEnd=t.end||"",this._localBuffer=t.buffer_days??7,this._dirty=!1}catch(t){this._error=l(t,this._lang)}}async _toggleEnabled(t){this._busy=!0,this._error="";try{let s=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/update",enabled:t});this._state=s}catch(s){this._error=l(s,this._lang)}finally{this._busy=!1}}async _save(){if(this._isAdmin){this._busy=!0,this._error="";try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/update",start:this._localStart||null,end:this._localEnd||null,buffer_days:this._localBuffer});this._state=t,this._dirty=!1}catch(t){this._error=l(t,this._lang)}finally{this._busy=!1}}}async _endNow(){if(this._isAdmin&&window.confirm(e("vacation_end_now_confirm",this._lang)||"End vacation immediately?")){this._busy=!0;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/end_now"});this._state=t,this._localStart=t.start||"",this._localEnd=t.end||""}catch(t){this._error=l(t,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_vacation"),window.dispatchEvent(new CustomEvent("location-changed"))}render(){let t=this._lang,s=this._state;if(!s)return n`<ha-card><div class="loading">${e("loading",t)||"Loading\u2026"}</div></ha-card>`;let p=s.is_active===!0,d=s.enabled===!0,u=s.exempt_task_ids?.length??0,b=p?e("vacation_status_active",t)||"Active now":d?e("vacation_status_scheduled",t)||"Scheduled":e("vacation_status_inactive",t)||"Inactive",g=p?"active":d?"scheduled":"inactive";return n`
<ha-card>
<div class="card-content">
<div class="header">
@@ -1,4 +1,4 @@
import"./chunks/chunk-LO2NM3CE.js";var A="maintenance-supporter",G=`ll-strategy-dashboard-${A}`,L="hui-maintenance-supporter-strategy-editor",I="NOT_RUNNING",P=[{status:"overdue",title:"Overdue",icon:"mdi:alert-circle",path:"overdue"},{status:"triggered",title:"Triggered",icon:"mdi:flash",path:"triggered"},{status:"due_soon",title:"Due Soon",icon:"mdi:clock-alert-outline",path:"due-soon"},{status:"ok",title:"OK",icon:"mdi:check-circle-outline",path:"ok"}],C=[{title:"Overdue",icon:"mdi:alert-circle",path:"overdue",filter:{filter_due_max_days:-1},matches:n=>n<=-1},{title:"Today",icon:"mdi:calendar-today",path:"today",filter:{filter_due_min_days:0,filter_due_max_days:0},matches:n=>n===0},{title:"This Week",icon:"mdi:calendar-week",path:"this-week",filter:{filter_due_min_days:1,filter_due_max_days:7},matches:n=>n>=1&&n<=7},{title:"This Month",icon:"mdi:calendar-month",path:"this-month",filter:{filter_due_min_days:8,filter_due_max_days:30},matches:n=>n>=8&&n<=30},{title:"Later",icon:"mdi:calendar-clock",path:"later",filter:{filter_due_min_days:31},matches:n=>n>=31}];function u(n){return{type:"grid",cards:[n]}}var H={condition:"view_columns",min:2};function V(){return{type:"markdown",text_only:!0,content:["\u{1F534} **{{ states('sensor.maintenance_supporter_overdue') }}** overdue","\u26A1 **{{ states('sensor.maintenance_supporter_triggered') }}** triggered","\u{1F7E1} **{{ states('sensor.maintenance_supporter_due_soon') }}** due soon","\u{1F7E2} **{{ states('sensor.maintenance_supporter_ok') }}** ok"].join(" \xB7 ")}}function N(){return{title:"Maintenance",type:"panel",cards:[{type:"empty-state",icon:"mdi:wrench-clock",icon_color:"primary",content_only:!0,title:"No maintenance objects yet",content:"Open the Maintenance panel to add your first object \u2014 pool pump, HVAC filter, vehicle, anything that needs scheduled care.",buttons:[{icon:"mdi:wrench",text:"Open Maintenance panel",appearance:"filled",variant:"brand",tap_action:{action:"navigate",navigation_path:"/maintenance-supporter"}},{icon:"mdi:plus",text:"Add object",appearance:"outlined",variant:"brand",tap_action:{action:"fire-dom-event",ll_custom:{type:"maintenance-supporter:add-object"}}}]}]}}function B(){let n=V();return{title:"Overview",icon:"mdi:wrench-clock",path:"overview",type:"sections",header:{layout:"responsive",card:n},sidebar:{sections:[{type:"grid",cards:[{type:"heading",heading:"Status",heading_style:"title"},n]}],visibility:[H],content_label:"Tasks",sidebar_label:"Status"},sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_status:["overdue","triggered","due_soon"]})]}}function U(n,i){let e=new Map;for(let a of n){let r=a.object.area_id||null;e.has(r)||e.set(r,[]),e.get(r).push(a)}let s=Array.from(e.keys()).filter(a=>a!==null);s.sort((a,r)=>{let c=i[a]?.name||a,l=i[r]?.name||r;return c.localeCompare(l)});let t=[];for(let a of s){let r=e.get(a);if(r.length===0)continue;let c=i[a];t.push({title:c?.name||a,icon:c?.icon||"mdi:floor-plan",path:`area-${a}`,type:"sections",sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:r.map(l=>l.object.name)})]})}let o=e.get(null);return o&&o.length>0&&t.push({title:"Unassigned",icon:"mdi:help-circle-outline",path:"unassigned",type:"sections",sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:o.map(a=>a.object.name)})]}),t}function $(n){let i=new Set;for(let s of n)for(let t of s.tasks||[])t.status&&i.add(t.status);let e=[];for(let s of P)i.has(s.status)&&e.push({title:s.title,icon:s.icon,path:s.path,type:"sections",sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_status:[s.status]})]});return e}function W(n,i,e){let s=new Map;for(let r of n){let c=r.object.area_id,l=c&&i[c]?.floor_id||null;s.has(l)||s.set(l,[]),s.get(l).push(r)}let t=Array.from(s.keys()).filter(r=>r!==null);t.sort((r,c)=>{let l=e[r],g=e[c],d=l?.level??0,p=g?.level??0;return d!==p?d-p:(l?.name||r).localeCompare(g?.name||c)});let o=[];for(let r of t){let c=s.get(r);if(c.length===0)continue;let l=e[r];o.push({title:l?.name||r,icon:l?.icon||"mdi:home-floor-1",path:`floor-${r}`,type:"sections",sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:c.map(g=>g.object.name)})]})}let a=s.get(null);return a&&a.length>0&&o.push({title:"Other",icon:"mdi:help-circle-outline",path:"other",type:"sections",sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:a.map(r=>r.object.name)})]}),o}function Y(n){return[{title:"Week",icon:"mdi:calendar-week",path:"cal-7",window_days:7},{title:"Fortnight",icon:"mdi:calendar-week-begin",path:"cal-14",window_days:14},{title:"Month",icon:"mdi:calendar-month",path:"cal-30",window_days:30},{title:"Year",icon:"mdi:calendar-clock",path:"cal-365",window_days:365}].map(e=>({title:e.title,icon:e.icon,path:e.path,type:"panel",cards:[{type:"custom:maintenance-supporter-calendar-card",window_days:e.window_days,show_window_chips:!1,show_user_filter:!0}]}))}Line truncated
import"./chunks/chunk-LO2NM3CE.js";var A="maintenance-supporter",I=`ll-strategy-dashboard-${A}`,L="hui-maintenance-supporter-strategy-editor",P="NOT_RUNNING",G=[{status:"overdue",title:"Overdue",icon:"mdi:alert-circle",path:"overdue"},{status:"triggered",title:"Triggered",icon:"mdi:flash",path:"triggered"},{status:"due_soon",title:"Due Soon",icon:"mdi:clock-alert-outline",path:"due-soon"},{status:"ok",title:"OK",icon:"mdi:check-circle-outline",path:"ok"}],C=[{title:"Overdue",icon:"mdi:alert-circle",path:"overdue",filter:{filter_due_max_days:-1},matches:i=>i<=-1},{title:"Today",icon:"mdi:calendar-today",path:"today",filter:{filter_due_min_days:0,filter_due_max_days:0},matches:i=>i===0},{title:"This Week",icon:"mdi:calendar-week",path:"this-week",filter:{filter_due_min_days:1,filter_due_max_days:7},matches:i=>i>=1&&i<=7},{title:"This Month",icon:"mdi:calendar-month",path:"this-month",filter:{filter_due_min_days:8,filter_due_max_days:30},matches:i=>i>=8&&i<=30},{title:"Later",icon:"mdi:calendar-clock",path:"later",filter:{filter_due_min_days:31},matches:i=>i>=31}];function u(i){return{type:"grid",cards:[i]}}var H={condition:"view_columns",min:2};function V(i={}){let n=e=>i[e]||`sensor.maintenance_supporter_${e}`;return{type:"markdown",text_only:!0,content:[`\u{1F534} **{{ states('${n("overdue")}') }}** overdue`,`\u26A1 **{{ states('${n("triggered")}') }}** triggered`,`\u{1F7E1} **{{ states('${n("due_soon")}') }}** due soon`,`\u{1F7E2} **{{ states('${n("ok")}') }}** ok`].join(" \xB7 ")}}function N(){return{title:"Maintenance",type:"panel",cards:[{type:"empty-state",icon:"mdi:wrench-clock",icon_color:"primary",content_only:!0,title:"No maintenance objects yet",content:"Open the Maintenance panel to add your first object \u2014 pool pump, HVAC filter, vehicle, anything that needs scheduled care.",buttons:[{icon:"mdi:wrench",text:"Open Maintenance panel",appearance:"filled",variant:"brand",tap_action:{action:"navigate",navigation_path:"/maintenance-supporter"}},{icon:"mdi:plus",text:"Add object",appearance:"outlined",variant:"brand",tap_action:{action:"fire-dom-event",ll_custom:{type:"maintenance-supporter:add-object"}}}]}]}}function B(i={}){let n=V(i);return{title:"Overview",icon:"mdi:wrench-clock",path:"overview",type:"sections",header:{layout:"responsive",card:n},sidebar:{sections:[{type:"grid",cards:[{type:"heading",heading:"Status",heading_style:"title"},n]}],visibility:[H],content_label:"Tasks",sidebar_label:"Status"},sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_status:["overdue","triggered","due_soon"]})]}}function $(i,n){let e=new Map;for(let r of i){let a=r.object.area_id||null;e.has(a)||e.set(a,[]),e.get(a).push(r)}let s=Array.from(e.keys()).filter(r=>r!==null);s.sort((r,a)=>{let c=n[r]?.name||r,d=n[a]?.name||a;return c.localeCompare(d)});let t=[];for(let r of s){let a=e.get(r);if(a.length===0)continue;let c=n[r];t.push({title:c?.name||r,icon:c?.icon||"mdi:floor-plan",path:`area-${r}`,type:"sections",sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:a.map(d=>d.object.name)})]})}let o=e.get(null);return o&&o.length>0&&t.push({title:"Unassigned",icon:"mdi:help-circle-outline",path:"unassigned",type:"sections",sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:o.map(r=>r.object.name)})]}),t}function U(i){let n=new Set;for(let s of i)for(let t of s.tasks||[])t.status&&n.add(t.status);let e=[];for(let s of G)n.has(s.status)&&e.push({title:s.title,icon:s.icon,path:s.path,type:"sections",sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_status:[s.status]})]});return e}function W(i,n,e){let s=new Map;for(let a of i){let c=a.object.area_id,d=c&&n[c]?.floor_id||null;s.has(d)||s.set(d,[]),s.get(d).push(a)}let t=Array.from(s.keys()).filter(a=>a!==null);t.sort((a,c)=>{let d=e[a],g=e[c],l=d?.level??0,p=g?.level??0;return l!==p?l-p:(d?.name||a).localeCompare(g?.name||c)});let o=[];for(let a of t){let c=s.get(a);if(c.length===0)continue;let d=e[a];o.push({title:d?.name||a,icon:d?.icon||"mdi:home-floor-1",path:`floor-${a}`,type:"sections",sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:c.map(g=>g.object.name)})]})}let r=s.get(null);return r&&r.length>0&&o.push({title:"Other",icon:"mdi:help-circle-outline",path:"other",type:"sections",sections:[u({type:"custom:maintenance-supporter-card",show_header:!1,filter_objects:r.map(a=>a.object.name)})]}),o}function Y(i){return[{title:"Week",icon:"mdi:calendar-week",path:"cal-7",window_days:7},{title:"Fortnight",icon:"mdi:calendar-week-begin",path:"cal-14",window_days:14},{title:"Month",icon:"mdi:calendar-month",path:"cal-30",window_days:30},{title:"Year",icon:"mdi:calendar-clock",path:"cal-365",window_days:365}].map(e=>({title:e.title,icon:e.icon,path:e.path,type:"panel",cards:[{type:"custom:maintenance-supporter-calendar-card",window_days:e.window_days,show_window_chips:!1,show_user_filter:!0}]}))}function F(i){let n=new Line truncated
<style>
:host, .editor { display: block; padding: 16px 0; }
label { display: block; font-weight: 500; margin-bottom: 8px; }
@@ -21,4 +21,4 @@ import"./chunks/chunk-LO2NM3CE.js";var A="maintenance-supporter",G=`ll-strategy-
The "Overview" view is always first. Empty groups are skipped.
</div>
</div>
`;let t=this.querySelector("#group-by");t?.addEventListener("change",()=>{let o={...this._config,group_by:t.value};this._config=o,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:o},bubbles:!0,composed:!0}))})}},O="maintenance-supporter-section",z=`ll-strategy-section-${O}`,b=class extends HTMLElement{static async generate(i,e){let s={type:"custom:maintenance-supporter-card",show_header:!1};i?.filter_status?.length&&(s.filter_status=i.filter_status),i?.max_items&&(s.max_items=i.max_items),i?.filter_due_min_days!==void 0&&(s.filter_due_min_days=i.filter_due_min_days),i?.filter_due_max_days!==void 0&&(s.filter_due_max_days=i.filter_due_max_days);let t=i?.filter_objects;if(i?.area_id&&!t)try{t=((await e.connection.sendMessagePromise({type:"maintenance_supporter/objects"})).objects||[]).filter(r=>r.object.area_id===i.area_id).map(r=>r.object.name)}catch{}t&&t.length>0&&(s.filter_objects=t);let o=[];return i?.title&&o.push({type:"heading",heading:i.title,heading_style:"title"}),o.push(s),{type:"grid",cards:o}}},x="2.3.6",K="[maintenance-supporter]";function E(n,i){try{return customElements.get(n)?"skipped":(customElements.define(n,i),"ok")}catch(e){return`ERROR: ${String(e)}`}}var D={editor:E(L,h),section:E(z,b)};console.log(`${K} strategy bundle v${x} loaded \u2014 registrations:`,D,"\u2014 run maintenanceSupporterDiagnose() in console for full state");window.maintenanceSupporterDiagnose=async()=>{let n=G,i=[];try{let e=document.querySelector("home-assistant");e?.hass&&(i=(await e.hass.connection.sendMessagePromise({type:"lovelace/resources"})).resources||[])}catch(e){i=[{url:`<error: ${String(e)}>`}]}return{version:x,el_registered:!!customElements.get(n),el_class_excerpt:customElements.get(n)?.toString().slice(0,100)||null,customStrategies_dashboard:window.customStrategies?.filter(e=>e.strategyType==="dashboard").map(e=>e.type)||[],scriptTagsInPage:Array.from(document.querySelectorAll("script[type=module]")).map(e=>e.src).filter(e=>/maint|strateg/i.test(e)),lovelaceResources_matching:i.filter(e=>/maint|strateg/i.test(e.url||"")).map(e=>e.url),registrations:D}};var w=class extends HTMLElement{static async generate(i,e){return await import("./chunks/vacation-section-card-YFJ6JUPY.js"),{type:"grid",cards:[{type:"custom:maintenance-vacation-section-card",title:i?.title}]}}},v=class extends HTMLElement{static async generate(i,e){return await import("./chunks/budget-section-card-V4BTQUYP.js"),{type:"grid",cards:[{type:"custom:maintenance-budget-section-card",title:i?.title}]}}},k=class extends HTMLElement{static async generate(i,e){return await import("./chunks/groups-section-card-U36TKQBF.js"),{type:"grid",cards:[{type:"custom:maintenance-groups-section-card",title:i?.title}]}}},S="ll-strategy-section-maintenance-supporter-vacation",M="ll-strategy-section-maintenance-supporter-budget",j="ll-strategy-section-maintenance-supporter-groups";customElements.get(S)||customElements.define(S,w);customElements.get(M)||customElements.define(M,v);customElements.get(j)||customElements.define(j,k);var f=null;function Q(){return f||(f=import("./chunks/dialog-mount-RTTXPFO3.js").catch(n=>{throw f=null,n})),f}var R="_msSupporterLlCustomBound";function m(n){history.pushState(null,"",n),window.dispatchEvent(new CustomEvent("location-changed"))}function X(){let n=window;n[R]||(n[R]=!0,document.addEventListener("ll-custom",i=>{let e=i.detail;if(!e||typeof e!="object")return;let s=e.ll_custom,t=s&&typeof s=="object"?s:e;if(!t||typeof t.type!="string"||!t.type.startsWith("maintenance-supporter:"))return;let o=t.type.slice(22);if(o==="deep-link"&&typeof t.path=="string"){m(t.path);return}(async()=>{let a;try{a=await Q()}catch{m("/maintenance-supporter");return}if(o==="add-object"){if(a.openCreateObjectDialog())return;m("/maintenance-supporter?ms_action=add_object");return}if(o==="open-task"&&typeof t.entry_id=="string"&&typeof t.task_id=="string"){if(a.openTaskQuickActions(t.entry_id,t.task_id)||a.openEditTaskDialog(t.entry_id,t.task_id))return;m(`/maintenance-supporter?entry_id=${encodeURIComponent(t.entry_id)}&task_id=${encodeURIComponent(t.task_id)}`);return}if(o==="open-object"&&typeof t.entry_id=="string"){if(a.openObjectQuickActions(t.entry_id))return;m(`/maintenance-supporter?entry_id=${encodeURIComponent(t.entry_id)}`);return}if(o==="edit-history"&&typeof t.entry_id=="string"&&typeof t.task_id=="string"&&typeof t.original_timestamp=="string"){let c=document.querySelector("home-assistant")?.hass;if(!c)return;try{let d=(await c.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:t.entry_id})).tasks?.find(p=>p.id===t.task_id)?.history?.find(p=>p.timestamp===t.original_timestamp);if(!d)return;a.openHistoryEditDialog({entry_id:t.entry_id,task_id:t.task_id,original_timestamp:t.original_timestamp,type:d.type||"completed",timestamp:d.timestamp||t.original_timestamp,notes:d.notes??null,cost:d.cost??null,duration:d.duration??null,completed_by:d.completed_by??null})}catch{m("/maintenance-suppor Line truncated
`;let t=this.querySelector("#group-by");t?.addEventListener("change",()=>{let o={...this._config,group_by:t.value};this._config=o,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:o},bubbles:!0,composed:!0}))})}},O="maintenance-supporter-section",z=`ll-strategy-section-${O}`,b=class extends HTMLElement{static async generate(n,e){let s={type:"custom:maintenance-supporter-card",show_header:!1};n?.filter_status?.length&&(s.filter_status=n.filter_status),n?.max_items&&(s.max_items=n.max_items),n?.filter_due_min_days!==void 0&&(s.filter_due_min_days=n.filter_due_min_days),n?.filter_due_max_days!==void 0&&(s.filter_due_max_days=n.filter_due_max_days);let t=n?.filter_objects;if(n?.area_id&&!t)try{t=((await e.connection.sendMessagePromise({type:"maintenance_supporter/objects"})).objects||[]).filter(a=>a.object.area_id===n.area_id).map(a=>a.object.name)}catch{}t&&t.length>0&&(s.filter_objects=t);let o=[];return n?.title&&o.push({type:"heading",heading:n.title,heading_style:"title"}),o.push(s),{type:"grid",cards:o}}},x="2.3.6",K="[maintenance-supporter]";function S(i,n){try{return customElements.get(i)?"skipped":(customElements.define(i,n),"ok")}catch(e){return`ERROR: ${String(e)}`}}var D={editor:S(L,h),section:S(z,b)};console.log(`${K} strategy bundle v${x} loaded \u2014 registrations:`,D,"\u2014 run maintenanceSupporterDiagnose() in console for full state");window.maintenanceSupporterDiagnose=async()=>{let i=I,n=[];try{let e=document.querySelector("home-assistant");e?.hass&&(n=(await e.hass.connection.sendMessagePromise({type:"lovelace/resources"})).resources||[])}catch(e){n=[{url:`<error: ${String(e)}>`}]}return{version:x,el_registered:!!customElements.get(i),el_class_excerpt:customElements.get(i)?.toString().slice(0,100)||null,customStrategies_dashboard:window.customStrategies?.filter(e=>e.strategyType==="dashboard").map(e=>e.type)||[],scriptTagsInPage:Array.from(document.querySelectorAll("script[type=module]")).map(e=>e.src).filter(e=>/maint|strateg/i.test(e)),lovelaceResources_matching:n.filter(e=>/maint|strateg/i.test(e.url||"")).map(e=>e.url),registrations:D}};var w=class extends HTMLElement{static async generate(n,e){return await import("./chunks/vacation-section-card-2434QSQ7.js"),{type:"grid",cards:[{type:"custom:maintenance-vacation-section-card",title:n?.title}]}}},v=class extends HTMLElement{static async generate(n,e){return await import("./chunks/budget-section-card-PVK5ECID.js"),{type:"grid",cards:[{type:"custom:maintenance-budget-section-card",title:n?.title}]}}},k=class extends HTMLElement{static async generate(n,e){return await import("./chunks/groups-section-card-VAKKX3RY.js"),{type:"grid",cards:[{type:"custom:maintenance-groups-section-card",title:n?.title}]}}},T="ll-strategy-section-maintenance-supporter-vacation",M="ll-strategy-section-maintenance-supporter-budget",j="ll-strategy-section-maintenance-supporter-groups";customElements.get(T)||customElements.define(T,w);customElements.get(M)||customElements.define(M,v);customElements.get(j)||customElements.define(j,k);var f=null;function Q(){return f||(f=import("./chunks/dialog-mount-3CFCNZ5R.js").catch(i=>{throw f=null,i})),f}var R="_msSupporterLlCustomBound";function m(i){history.pushState(null,"",i),window.dispatchEvent(new CustomEvent("location-changed"))}function X(){let i=window;i[R]||(i[R]=!0,document.addEventListener("ll-custom",n=>{let e=n.detail;if(!e||typeof e!="object")return;let s=e.ll_custom,t=s&&typeof s=="object"?s:e;if(!t||typeof t.type!="string"||!t.type.startsWith("maintenance-supporter:"))return;let o=t.type.slice(22);if(o==="deep-link"&&typeof t.path=="string"){m(t.path);return}(async()=>{let r;try{r=await Q()}catch{m("/maintenance-supporter");return}if(o==="add-object"){if(r.openCreateObjectDialog())return;m("/maintenance-supporter?ms_action=add_object");return}if(o==="open-task"&&typeof t.entry_id=="string"&&typeof t.task_id=="string"){if(r.openTaskQuickActions(t.entry_id,t.task_id)||r.openEditTaskDialog(t.entry_id,t.task_id))return;m(`/maintenance-supporter?entry_id=${encodeURIComponent(t.entry_id)}&task_id=${encodeURIComponent(t.task_id)}`);return}if(o==="open-object"&&typeof t.entry_id=="string"){if(r.openObjectQuickActions(t.entry_id))return;m(`/maintenance-supporter?entry_id=${encodeURIComponent(t.entry_id)}`);return}if(o==="edit-history"&&typeof t.entry_id=="string"&&typeof t.task_id=="string"&&typeof t.original_timestamp=="string"){let c=document.querySelector("home-assistant")?.hass;if(!c)return;try{let l=(await c.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:t.entry_id})).tasks?.find(p=>p.id===t.task_id)?.history?.find(p=>p.timestamp===t.original_timestamp);if(!l)return;r.openHistoryEditDialog({entry_id:t.entry_id,task_id:t.task_id,original_timestamp:t.original_timestamp,type:l.type||"completed",timestamp:l.timestamp||t.original_timestamp,notes:l.notes??null,cost:l.cost??null,duration:l.duration??null,completed_by:l.completed_by??null})}catch{m("/maintenance-suppor Line truncated
Loaded 100 of 227 files, more files were not shown because too many files have changed in this diff. Show more