361 files
This commit is contained in:
1 parent
d2c5af43e3
commit
bf886c9347
362 files changed
+22035
-1260
No files matched your search
+1
-1
@@ -1 +1 @@
|
||||
{"pid": 71, "version": 1, "ha_version": "2026.9.4", "start_ts": 1791146884.4391842}
|
||||
{"pid": 70, "version": 1, "ha_version": "2026.9.4", "start_ts": 1791306689.7190704}
|
||||
@@ -196,7 +196,7 @@
|
||||
},
|
||||
{
|
||||
"id": "e92ef0caff41454e9f49ea966ed99e41",
|
||||
"url": "/hacsfiles/lovelace-multiple-entity-row/multiple-entity-row.js?hacstag=1789210374120",
|
||||
"url": "/hacsfiles/lovelace-multiple-entity-row/multiple-entity-row.js?hacstag=1789210374130",
|
||||
"type": "module"
|
||||
},
|
||||
{
|
||||
@@ -256,7 +256,7 @@
|
||||
},
|
||||
{
|
||||
"id": "8713ea9c008e4270bb28085eed5be959",
|
||||
"url": "/hacsfiles/Statistics-Graph-Chart-Card/statistics-graph-chart-card.js?hacstag=1034932856404",
|
||||
"url": "/hacsfiles/Statistics-Graph-Chart-Card/statistics-graph-chart-card.js?hacstag=1034932856405",
|
||||
"type": "module"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -516,16 +516,26 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
|
||||
_register_photo_services(hass)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
# Reload when the options flow changes the entry (e.g. a new Immich
|
||||
# selection or the reverse-geocode toggle) so it takes effect at once.
|
||||
entry.async_on_unload(entry.add_update_listener(_async_update_listener))
|
||||
|
||||
store.notify()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
domain_data = hass.data.get(DOMAIN, {})
|
||||
coordinator = domain_data.get(entry.entry_id, {}).get("coordinator")
|
||||
if coordinator is not None:
|
||||
await coordinator._cancel_enrichment()
|
||||
domain_data.pop(entry.entry_id, None)
|
||||
# Drop the shared semaphore once the last album is gone so it's
|
||||
# re-created if the integration is re-added later.
|
||||
|
||||
@@ -5,6 +5,7 @@ from collections import OrderedDict, deque
|
||||
from dataclasses import dataclass, replace
|
||||
from datetime import datetime, timezone
|
||||
import logging
|
||||
import math
|
||||
import random
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
@@ -20,6 +21,10 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
CAMERA_METADATA_FIELDS,
|
||||
DEFAULT_CUSTOM_LOOKBACK_DAYS,
|
||||
DEFAULT_SHUFFLE_AGE_BIAS,
|
||||
DEFAULT_MISSING_DATE_MODE,
|
||||
MAX_RESOLUTION_SHORT_EDGE,
|
||||
ORIENTATION_MISMATCH_PAIR,
|
||||
ORIENTATION_MISMATCH_AVOID,
|
||||
@@ -111,6 +116,41 @@ def _utc_now_iso() -> str:
|
||||
return datetime.now(timezone.utc).isoformat()
|
||||
|
||||
|
||||
def _camera_metadata_attributes(item: MediaItem | None) -> dict[str, Any]:
|
||||
metadata = getattr(item, "camera_metadata", None)
|
||||
return {
|
||||
field: metadata.get(field) if isinstance(metadata, dict) else None
|
||||
for field in CAMERA_METADATA_FIELDS
|
||||
}
|
||||
|
||||
|
||||
def _item_faces(item: MediaItem | None) -> tuple[ip.FaceBox, ...] | None:
|
||||
"""Return the validated face boxes stored on a media item.
|
||||
|
||||
None means no face data; an empty tuple means the photo has no faces.
|
||||
"""
|
||||
faces = getattr(item, "faces", None) if item is not None else None
|
||||
if not isinstance(faces, (list, tuple)):
|
||||
return None
|
||||
valid: list[ip.FaceBox] = []
|
||||
for face in faces:
|
||||
if (
|
||||
not isinstance(face, (list, tuple))
|
||||
or len(face) not in (5, 6)
|
||||
or not all(
|
||||
not isinstance(value, bool) and isinstance(value, (int, float))
|
||||
and math.isfinite(value) for value in face[:5]
|
||||
)
|
||||
or (len(face) == 6 and not isinstance(face[5], bool))
|
||||
):
|
||||
continue
|
||||
left, top, right, bottom, weight = (float(value) for value in face[:5])
|
||||
if 0 <= left < right <= 1 and 0 <= top < bottom <= 1 and weight > 0:
|
||||
selected = face[5] if len(face) == 6 else False
|
||||
valid.append(ip.FaceBox(left, top, right, bottom, weight, selected))
|
||||
return tuple(valid)
|
||||
|
||||
|
||||
class _DownloadCache:
|
||||
"""Byte-budget LRU cache for downloaded image data, O(1) per operation."""
|
||||
|
||||
@@ -208,6 +248,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
self._frame_id: int = 0
|
||||
|
||||
self._interrupt_event: asyncio.Event = asyncio.Event()
|
||||
self._slide_deadline: float | None = None
|
||||
# Navigation runs directly in the button/service coroutine. The lock
|
||||
# serialises rapid presses while buffered swaps remain independent of
|
||||
# the background timer loop.
|
||||
@@ -244,6 +285,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
def _on_store_change() -> None:
|
||||
self._download_cache.resize(self.store.image_cache_mb * 1024 * 1024)
|
||||
self._effective_cache = None
|
||||
self._slide_deadline = None
|
||||
self._invalidate_timeline()
|
||||
|
||||
store.add_listener(_on_store_change)
|
||||
@@ -333,12 +375,14 @@ class AlbumSlideshowCamera(Camera):
|
||||
"captured_at_primary": captured_at,
|
||||
"uploaded_at": _ts_to_iso(getattr(cur, "uploaded_at", None)),
|
||||
"byte_size": getattr(cur, "byte_size", None),
|
||||
# GPS + reverse-geocoded label come from EXIF for local-folder
|
||||
# entries; Google albums leave these as ``None``.
|
||||
"latitude": getattr(cur, "latitude", None),
|
||||
"longitude": getattr(cur, "longitude", None),
|
||||
"location": getattr(cur, "location", None),
|
||||
"description": getattr(cur, "description", None),
|
||||
**_camera_metadata_attributes(cur),
|
||||
"google_metadata_enabled": bool(getattr(self.coordinator, "google_metadata_enabled", False)),
|
||||
"google_location_enabled": bool(getattr(self.coordinator, "google_location_enabled", False)),
|
||||
"google_reverse_geocode_enabled": bool(getattr(self.coordinator, "google_reverse_geocode_enabled", False)),
|
||||
# Structured per-image caption metadata. A single-element list for
|
||||
# normal slides; two elements (top/left first) for paired slides,
|
||||
# so the card can overlay an accurate date/location on each half.
|
||||
@@ -351,6 +395,8 @@ class AlbumSlideshowCamera(Camera):
|
||||
"portrait_mode": self.store.portrait_mode,
|
||||
"order_mode": self.store.order_mode,
|
||||
"date_filter": self.store.date_filter,
|
||||
"custom_lookback_days": int(self.store.custom_lookback_days),
|
||||
"shuffle_age_bias": int(self.store.shuffle_age_bias),
|
||||
"missing_date_mode": self.store.missing_date_mode,
|
||||
"paused": bool(self.store.paused),
|
||||
"refresh_hours": int(self.store.refresh_hours),
|
||||
@@ -393,6 +439,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
"latitude": getattr(cur, "latitude", None),
|
||||
"longitude": getattr(cur, "longitude", None),
|
||||
"description": getattr(cur, "description", None),
|
||||
**_camera_metadata_attributes(cur),
|
||||
}
|
||||
]
|
||||
|
||||
@@ -429,6 +476,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
cache_key = (
|
||||
id(raw),
|
||||
self.store.date_filter,
|
||||
getattr(self.store, "custom_lookback_days", DEFAULT_CUSTOM_LOOKBACK_DAYS),
|
||||
self.store.missing_date_mode,
|
||||
self.store.order_mode,
|
||||
self.store.hidden_revision,
|
||||
@@ -440,6 +488,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
[item for item in raw if item.photo_id and item.photo_id not in self.store.hidden_photo_ids]
|
||||
if self.store.hidden_photo_ids else raw,
|
||||
mode=self.store.date_filter,
|
||||
lookback_days=getattr(self.store, "custom_lookback_days", DEFAULT_CUSTOM_LOOKBACK_DAYS),
|
||||
missing_date=self.store.missing_date_mode,
|
||||
)
|
||||
ordered = playlist.order_items(filtered, self.store.order_mode)
|
||||
@@ -463,6 +512,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
self._last_nav_outcome = "pending"
|
||||
self._last_nav_error = None
|
||||
# Wake the timer loop so the manual frame starts a fresh interval.
|
||||
self._slide_deadline = None
|
||||
self._interrupt_event.set()
|
||||
self.async_write_ha_state()
|
||||
|
||||
@@ -492,6 +542,8 @@ class AlbumSlideshowCamera(Camera):
|
||||
)
|
||||
finally:
|
||||
self._navigation_pending -= 1
|
||||
self._slide_deadline = None
|
||||
self._interrupt_event.set()
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_force_refresh(self) -> None:
|
||||
@@ -1015,6 +1067,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
|
||||
async def _render_loop(self, initial_delay: float = 0.0) -> None:
|
||||
"""Display buffered frames on command/timer and refill them in the background."""
|
||||
loop = asyncio.get_running_loop()
|
||||
if initial_delay > 0:
|
||||
await asyncio.sleep(initial_delay)
|
||||
|
||||
@@ -1045,6 +1098,8 @@ class AlbumSlideshowCamera(Camera):
|
||||
|
||||
while True:
|
||||
try:
|
||||
if self._slide_deadline is None:
|
||||
self._slide_deadline = loop.time() + float(int(self.store.slide_interval))
|
||||
if self._timeline_dirty:
|
||||
async with self._navigation_lock:
|
||||
if self._timeline_dirty:
|
||||
@@ -1052,16 +1107,22 @@ class AlbumSlideshowCamera(Camera):
|
||||
continue
|
||||
|
||||
interrupted = await self._wait_or_interrupt(
|
||||
float(int(self.store.slide_interval))
|
||||
max(0.0, self._slide_deadline - loop.time())
|
||||
)
|
||||
if not interrupted and not self.store.paused:
|
||||
async with self._navigation_lock:
|
||||
if not self._timeline_dirty:
|
||||
if (
|
||||
not self._timeline_dirty
|
||||
and self._slide_deadline is not None
|
||||
and loop.time() >= self._slide_deadline
|
||||
):
|
||||
await self._show_next_frame()
|
||||
self._slide_deadline = loop.time() + float(int(self.store.slide_interval))
|
||||
self._consecutive_failures = 0
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception as err:
|
||||
self._slide_deadline = None
|
||||
self._consecutive_failures += 1
|
||||
_LOGGER.warning(
|
||||
"Album Slideshow: buffered navigation/render failed (attempt %d): %s",
|
||||
@@ -1104,7 +1165,8 @@ class AlbumSlideshowCamera(Camera):
|
||||
self._index = (self._index + 1) % count
|
||||
return
|
||||
|
||||
self._index = self._next_random_index(count)
|
||||
bias = getattr(self.store, "shuffle_age_bias", DEFAULT_SHUFFLE_AGE_BIAS)
|
||||
self._index = self._next_age_weighted_index(items, bias) if bias else self._next_random_index(count)
|
||||
cur_url = items[self._index].url
|
||||
self._recent_urls.append(cur_url)
|
||||
keep = min(20, max(1, count - 1))
|
||||
@@ -1122,6 +1184,13 @@ class AlbumSlideshowCamera(Camera):
|
||||
return
|
||||
self._index = (self._index + 1) % count
|
||||
|
||||
def _crop_hints(self, item: MediaItem | None) -> ip.CropHints:
|
||||
return ip.CropHints(
|
||||
faces=_item_faces(item),
|
||||
debug=bool(getattr(getattr(self, "store", None), "face_debug", False)),
|
||||
name=getattr(item, "filename", None),
|
||||
)
|
||||
|
||||
async def _compose_for_index(
|
||||
self, items: list[MediaItem]
|
||||
) -> tuple[Image.Image | None, dict | None]:
|
||||
@@ -1183,6 +1252,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
composed = await self._async_image_job(
|
||||
ip.pair_images, img, other_img, width, height, fill_mode,
|
||||
is_portrait_canvas, divider, divider_fill, transparent_divider,
|
||||
self._crop_hints(cur), self._crop_hints(other_item),
|
||||
)
|
||||
pair_frames = [
|
||||
{
|
||||
@@ -1191,6 +1261,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
"latitude": getattr(cur, "latitude", None),
|
||||
"longitude": getattr(cur, "longitude", None),
|
||||
"description": getattr(cur, "description", None),
|
||||
**_camera_metadata_attributes(cur),
|
||||
},
|
||||
{
|
||||
"captured_at": _ts_to_iso(getattr(other_item, "captured_at", None)),
|
||||
@@ -1198,12 +1269,13 @@ class AlbumSlideshowCamera(Camera):
|
||||
"latitude": getattr(other_item, "latitude", None),
|
||||
"longitude": getattr(other_item, "longitude", None),
|
||||
"description": getattr(other_item, "description", None),
|
||||
**_camera_metadata_attributes(other_item),
|
||||
},
|
||||
]
|
||||
pair_meta = [f["captured_at"] for f in pair_frames]
|
||||
else:
|
||||
composed = await self._async_image_job(
|
||||
ip.render_image, img, fill_mode, width, height,
|
||||
ip.render_image, img, fill_mode, width, height, self._crop_hints(cur),
|
||||
)
|
||||
finally:
|
||||
ip.safe_close(other_img)
|
||||
@@ -1226,7 +1298,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
return composed, meta
|
||||
|
||||
composed = await self._async_image_job(
|
||||
ip.render_image, img, fill_mode, width, height
|
||||
ip.render_image, img, fill_mode, width, height, self._crop_hints(cur)
|
||||
)
|
||||
return composed, {
|
||||
"is_portrait": cur_is_portrait,
|
||||
@@ -1278,7 +1350,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
if self._index != start:
|
||||
self._do_advance(count, items)
|
||||
composed = await self._async_image_job(
|
||||
ip.render_image, img, fill_mode, width, height
|
||||
ip.render_image, img, fill_mode, width, height, self._crop_hints(cur)
|
||||
)
|
||||
return composed, {
|
||||
"is_portrait": is_portrait_canvas,
|
||||
@@ -1304,7 +1376,7 @@ class AlbumSlideshowCamera(Camera):
|
||||
try:
|
||||
cur_is_portrait = ip.is_portrait_item(item, img)
|
||||
composed = await self._async_image_job(
|
||||
ip.render_image, img, fill_mode, width, height
|
||||
ip.render_image, img, fill_mode, width, height, self._crop_hints(item)
|
||||
)
|
||||
return composed, {
|
||||
"is_portrait": cur_is_portrait,
|
||||
@@ -1407,6 +1479,23 @@ class AlbumSlideshowCamera(Camera):
|
||||
|
||||
return None
|
||||
|
||||
def _next_age_weighted_index(self, items: list[MediaItem], bias: int) -> int:
|
||||
weights = playlist.age_weights(
|
||||
items, bias, missing_date=getattr(self.store, "missing_date_mode", DEFAULT_MISSING_DATE_MODE),
|
||||
)
|
||||
count = len(items)
|
||||
if count <= 1 or min(weights) == max(weights):
|
||||
return self._next_random_index(count)
|
||||
cooldown = min(5, max(1, count // 10))
|
||||
recent = set(self._recent_urls[-cooldown:])
|
||||
candidates = [
|
||||
index for index, item in enumerate(items)
|
||||
if index != self._index and item.url not in recent
|
||||
]
|
||||
if not candidates:
|
||||
candidates = [index for index in range(count) if index != self._index]
|
||||
return self._rng.choices(candidates, weights=[weights[index] for index in candidates], k=1)[0]
|
||||
|
||||
def _next_random_index(self, count: int) -> int:
|
||||
if count <= 1:
|
||||
self._random_order = [0]
|
||||
|
||||
@@ -20,6 +20,11 @@ from .const import (
|
||||
CONF_PROVIDER,
|
||||
CONF_ALBUM_NAME,
|
||||
CONF_ALBUM_URL,
|
||||
CONF_GOOGLE_METADATA,
|
||||
DEFAULT_GOOGLE_METADATA,
|
||||
CONF_GOOGLE_LOCATION,
|
||||
DEFAULT_GOOGLE_LOCATION,
|
||||
DEFAULT_GOOGLE_REVERSE_GEOCODE,
|
||||
CONF_LOCAL_PATH,
|
||||
CONF_MEDIA_CONTENT_ID,
|
||||
CONF_RECURSIVE,
|
||||
@@ -149,6 +154,193 @@ def _redact_token(token: str | None) -> str:
|
||||
return f"{token[:3]}...({len(token)} chars, shape {shape})"
|
||||
|
||||
|
||||
async def _fetch_immich_choices(
|
||||
client: Any,
|
||||
) -> tuple[dict[str, str], dict[str, str]]:
|
||||
"""Return id -> name maps of the albums and named people a key can see.
|
||||
|
||||
A key with limited permissions can read the server but not albums or
|
||||
people; keep going with whatever it can see.
|
||||
"""
|
||||
albums: list[dict[str, Any]] = []
|
||||
people: list[dict[str, Any]] = []
|
||||
for label, call in (
|
||||
("albums", client.async_list_albums),
|
||||
("people", client.async_list_people),
|
||||
):
|
||||
try:
|
||||
result = await call()
|
||||
except Exception as err: # noqa: BLE001
|
||||
_LOGGER.warning(
|
||||
"Immich %s listing failed for %s: %s",
|
||||
label,
|
||||
client.base_url,
|
||||
_describe_error(err),
|
||||
)
|
||||
else:
|
||||
if label == "albums":
|
||||
albums = result
|
||||
else:
|
||||
people = result
|
||||
album_map = {
|
||||
a["id"]: (a.get("albumName") or a["id"]) for a in albums if a.get("id")
|
||||
}
|
||||
people_map = {
|
||||
p["id"]: p["name"]
|
||||
for p in people
|
||||
if p.get("id") and (p.get("name") or "").strip()
|
||||
}
|
||||
return album_map, people_map
|
||||
|
||||
|
||||
def _immich_validation_error(err: BaseException) -> str:
|
||||
return (
|
||||
"immich_invalid_auth"
|
||||
if getattr(err, "status", None) in (401, 403)
|
||||
else "immich_cannot_connect"
|
||||
)
|
||||
|
||||
|
||||
def _immich_select_schema(
|
||||
albums: dict[str, str],
|
||||
people: dict[str, str],
|
||||
defaults: dict[str, Any] | None = None,
|
||||
) -> vol.Schema:
|
||||
"""Build the albums/people/favorites picker, optionally pre-filled."""
|
||||
defaults = defaults or {}
|
||||
|
||||
def _default(key: str, fallback: Any = vol.UNDEFINED) -> Any:
|
||||
return defaults.get(key, fallback)
|
||||
|
||||
fields: dict[Any, Any] = {
|
||||
vol.Required(CONF_ALBUM_NAME, default=_default(CONF_ALBUM_NAME)): str
|
||||
}
|
||||
if albums:
|
||||
album_options = [
|
||||
selector.SelectOptionDict(value=_ALL_ALBUMS, label="Select all albums")
|
||||
] + [
|
||||
selector.SelectOptionDict(value=aid, label=name)
|
||||
for aid, name in albums.items()
|
||||
]
|
||||
fields[vol.Optional("albums", default=_default("albums"))] = (
|
||||
selector.SelectSelector(
|
||||
selector.SelectSelectorConfig(
|
||||
options=album_options,
|
||||
multiple=True,
|
||||
mode=selector.SelectSelectorMode.DROPDOWN,
|
||||
custom_value=False,
|
||||
)
|
||||
)
|
||||
)
|
||||
if people:
|
||||
people_options = [
|
||||
selector.SelectOptionDict(value=_ALL_PEOPLE, label="Select all people")
|
||||
] + [
|
||||
selector.SelectOptionDict(value=pid, label=name)
|
||||
for pid, name in people.items()
|
||||
]
|
||||
fields[vol.Optional("people", default=_default("people"))] = (
|
||||
selector.SelectSelector(
|
||||
selector.SelectSelectorConfig(
|
||||
options=people_options,
|
||||
multiple=True,
|
||||
mode=selector.SelectSelectorMode.DROPDOWN,
|
||||
custom_value=False,
|
||||
)
|
||||
)
|
||||
)
|
||||
fields[vol.Optional("favorites", default=_default("favorites", False))] = (
|
||||
selector.BooleanSelector()
|
||||
)
|
||||
filter_default = _default(CONF_IMMICH_FILTER)
|
||||
fields[
|
||||
vol.Optional(
|
||||
CONF_IMMICH_FILTER,
|
||||
description=(
|
||||
{"suggested_value": filter_default}
|
||||
if filter_default is not vol.UNDEFINED
|
||||
else None
|
||||
),
|
||||
)
|
||||
] = str
|
||||
fields[
|
||||
vol.Optional(
|
||||
CONF_IMMICH_IMAGE_SIZE,
|
||||
default=_default(CONF_IMMICH_IMAGE_SIZE, DEFAULT_IMMICH_IMAGE_SIZE),
|
||||
)
|
||||
] = vol.In(IMMICH_IMAGE_SIZE_OPTIONS)
|
||||
return vol.Schema(fields)
|
||||
|
||||
|
||||
def _parse_immich_select(
|
||||
user_input: dict[str, Any],
|
||||
albums: dict[str, str],
|
||||
people: dict[str, str],
|
||||
) -> tuple[dict[str, Any], dict[str, str]]:
|
||||
"""Turn picker input into entry data fields; return ``(fields, errors)``."""
|
||||
errors: dict[str, str] = {}
|
||||
name = user_input[CONF_ALBUM_NAME].strip()
|
||||
size = user_input.get(CONF_IMMICH_IMAGE_SIZE, DEFAULT_IMMICH_IMAGE_SIZE)
|
||||
raw_filter = (user_input.get(CONF_IMMICH_FILTER) or "").strip()
|
||||
favorites = bool(user_input.get("favorites"))
|
||||
|
||||
chosen_albums = [a for a in user_input.get("albums", []) if a]
|
||||
if _ALL_ALBUMS in chosen_albums:
|
||||
chosen_albums = list(albums.keys())
|
||||
else:
|
||||
chosen_albums = [a for a in chosen_albums if a in albums]
|
||||
|
||||
chosen_people = [p for p in user_input.get("people", []) if p]
|
||||
if _ALL_PEOPLE in chosen_people:
|
||||
chosen_people = list(people.keys())
|
||||
else:
|
||||
chosen_people = [p for p in chosen_people if p in people]
|
||||
|
||||
if raw_filter:
|
||||
try:
|
||||
parsed = json.loads(raw_filter)
|
||||
if not isinstance(parsed, dict):
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
errors[CONF_IMMICH_FILTER] = "immich_filter_invalid"
|
||||
|
||||
selection = {
|
||||
"albums": chosen_albums,
|
||||
"people": chosen_people,
|
||||
"favorites": favorites,
|
||||
}
|
||||
fields = {
|
||||
CONF_IMMICH_SELECTION_TYPE: IMMICH_SELECTION_COMPOSITE,
|
||||
CONF_IMMICH_SELECTION_ID: json.dumps(selection, sort_keys=True),
|
||||
CONF_IMMICH_IMAGE_SIZE: size,
|
||||
CONF_ALBUM_NAME: name,
|
||||
CONF_IMMICH_FILTER: raw_filter,
|
||||
}
|
||||
return fields, errors
|
||||
|
||||
|
||||
def _immich_current_selection(data: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Map an entry's stored Immich selection onto the picker's fields.
|
||||
|
||||
Composite entries map one to one. Older single-type entries are
|
||||
translated where the picker can express them (albums, people,
|
||||
favorites); ``all``/``random``/``search`` start with nothing ticked,
|
||||
which the picker saves as "all photos" (plus any stored filter).
|
||||
"""
|
||||
from . import immich as immich_api
|
||||
|
||||
sel_type = data.get(CONF_IMMICH_SELECTION_TYPE)
|
||||
sel_id = data.get(CONF_IMMICH_SELECTION_ID) or ""
|
||||
if sel_type == IMMICH_SELECTION_COMPOSITE:
|
||||
return immich_api.parse_composite_selection(sel_id)
|
||||
ids = [part for part in sel_id.split(",") if part]
|
||||
return {
|
||||
"albums": ids if sel_type in ("album", "albums") else [],
|
||||
"people": ids if sel_type in ("person", "people") else [],
|
||||
"favorites": sel_type == "favorites",
|
||||
}
|
||||
|
||||
|
||||
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
VERSION = 1
|
||||
|
||||
@@ -190,9 +382,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
) -> config_entries.OptionsFlow:
|
||||
"""Return the options flow handler.
|
||||
|
||||
Only local-folder entries expose user-tunable options today (the
|
||||
reverse-geocode toggle); Google entries get a no-op handler so
|
||||
that the "Configure" button doesn't appear empty in the UI.
|
||||
Local-folder, Nextcloud and Ente entries expose the reverse-geocode
|
||||
toggle; Immich entries reopen the albums/people/favorites picker.
|
||||
Other providers get a no-op handler so that the "Configure" button
|
||||
doesn't appear empty in the UI.
|
||||
|
||||
Note: do NOT pass ``config_entry`` to the OptionsFlow constructor.
|
||||
Since Home Assistant 2024.12 the base class manages
|
||||
@@ -206,6 +399,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
PROVIDER_ENTE,
|
||||
):
|
||||
return LocalFolderOptionsFlow()
|
||||
if config_entry.data.get(CONF_PROVIDER) == PROVIDER_IMMICH:
|
||||
return ImmichOptionsFlow()
|
||||
if config_entry.data.get(CONF_PROVIDER) == PROVIDER_GOOGLE_SHARED:
|
||||
return GoogleOptionsFlow()
|
||||
return _NoOptionsFlow()
|
||||
|
||||
async def async_step_user(self, user_input: dict[str, Any] | None = None) -> FlowResult:
|
||||
@@ -354,8 +551,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
from . import immich as immich_api
|
||||
|
||||
client = immich_api.ImmichClient(self.hass, url, key)
|
||||
albums: list[dict[str, Any]] = []
|
||||
people: list[dict[str, Any]] = []
|
||||
try:
|
||||
await client.async_validate()
|
||||
except Exception as err: # noqa: BLE001 - any failure means bad URL/key
|
||||
@@ -364,46 +559,14 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
client.base_url,
|
||||
_describe_error(err),
|
||||
)
|
||||
errors["base"] = (
|
||||
"immich_invalid_auth"
|
||||
if getattr(err, "status", None) in (401, 403)
|
||||
else "immich_cannot_connect"
|
||||
)
|
||||
errors["base"] = _immich_validation_error(err)
|
||||
else:
|
||||
# A key with limited permissions can read the server but not
|
||||
# albums or people; keep going with whatever it can see.
|
||||
for label, call in (
|
||||
("albums", client.async_list_albums),
|
||||
("people", client.async_list_people),
|
||||
):
|
||||
try:
|
||||
result = await call()
|
||||
except Exception as err: # noqa: BLE001
|
||||
_LOGGER.warning(
|
||||
"Immich %s listing failed for %s: %s",
|
||||
label,
|
||||
client.base_url,
|
||||
_describe_error(err),
|
||||
)
|
||||
else:
|
||||
if label == "albums":
|
||||
albums = result
|
||||
else:
|
||||
people = result
|
||||
|
||||
self._immich_url = client.base_url
|
||||
self._immich_key = key
|
||||
# id -> name maps for the two multi-select pickers.
|
||||
self._immich_albums = {
|
||||
a["id"]: (a.get("albumName") or a["id"])
|
||||
for a in albums
|
||||
if a.get("id")
|
||||
}
|
||||
self._immich_people = {
|
||||
p["id"]: p["name"]
|
||||
for p in people
|
||||
if p.get("id") and (p.get("name") or "").strip()
|
||||
}
|
||||
(
|
||||
self._immich_albums,
|
||||
self._immich_people,
|
||||
) = await _fetch_immich_choices(client)
|
||||
return await self.async_step_immich_select()
|
||||
|
||||
schema = vol.Schema(
|
||||
@@ -428,41 +591,15 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
errors: dict[str, str] = {}
|
||||
|
||||
if user_input is not None:
|
||||
name = user_input[CONF_ALBUM_NAME].strip()
|
||||
size = user_input.get(CONF_IMMICH_IMAGE_SIZE, DEFAULT_IMMICH_IMAGE_SIZE)
|
||||
raw_filter = (user_input.get(CONF_IMMICH_FILTER) or "").strip()
|
||||
favorites = bool(user_input.get("favorites"))
|
||||
|
||||
chosen_albums = [a for a in user_input.get("albums", []) if a]
|
||||
if _ALL_ALBUMS in chosen_albums:
|
||||
chosen_albums = list(self._immich_albums.keys())
|
||||
else:
|
||||
chosen_albums = [a for a in chosen_albums if a in self._immich_albums]
|
||||
|
||||
chosen_people = [p for p in user_input.get("people", []) if p]
|
||||
if _ALL_PEOPLE in chosen_people:
|
||||
chosen_people = list(self._immich_people.keys())
|
||||
else:
|
||||
chosen_people = [p for p in chosen_people if p in self._immich_people]
|
||||
|
||||
if raw_filter:
|
||||
try:
|
||||
parsed = json.loads(raw_filter)
|
||||
if not isinstance(parsed, dict):
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
errors[CONF_IMMICH_FILTER] = "immich_filter_invalid"
|
||||
|
||||
fields, errors = _parse_immich_select(
|
||||
user_input, self._immich_albums, self._immich_people
|
||||
)
|
||||
if not errors:
|
||||
selection = {
|
||||
"albums": chosen_albums,
|
||||
"people": chosen_people,
|
||||
"favorites": favorites,
|
||||
}
|
||||
sel_id = json.dumps(selection, sort_keys=True)
|
||||
name = fields[CONF_ALBUM_NAME]
|
||||
raw_filter = fields.pop(CONF_IMMICH_FILTER)
|
||||
unique = (
|
||||
f"{DOMAIN}:{PROVIDER_IMMICH}:{self._immich_url}:"
|
||||
f"composite:{sel_id}:{raw_filter}:{name}"
|
||||
f"composite:{fields[CONF_IMMICH_SELECTION_ID]}:{raw_filter}:{name}"
|
||||
)
|
||||
await self.async_set_unique_id(unique)
|
||||
self._abort_if_unique_id_configured()
|
||||
@@ -470,58 +607,18 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
CONF_PROVIDER: PROVIDER_IMMICH,
|
||||
CONF_IMMICH_URL: self._immich_url,
|
||||
CONF_IMMICH_API_KEY: self._immich_key,
|
||||
CONF_IMMICH_SELECTION_TYPE: IMMICH_SELECTION_COMPOSITE,
|
||||
CONF_IMMICH_SELECTION_ID: sel_id,
|
||||
CONF_IMMICH_IMAGE_SIZE: size,
|
||||
CONF_ALBUM_NAME: name,
|
||||
**fields,
|
||||
}
|
||||
if raw_filter:
|
||||
data[CONF_IMMICH_FILTER] = raw_filter
|
||||
return self.async_create_entry(title=name, data=data)
|
||||
|
||||
fields: dict[Any, Any] = {vol.Required(CONF_ALBUM_NAME): str}
|
||||
if self._immich_albums:
|
||||
album_options = [
|
||||
selector.SelectOptionDict(
|
||||
value=_ALL_ALBUMS, label="Select all albums"
|
||||
)
|
||||
] + [
|
||||
selector.SelectOptionDict(value=aid, label=name)
|
||||
for aid, name in self._immich_albums.items()
|
||||
]
|
||||
fields[vol.Optional("albums")] = selector.SelectSelector(
|
||||
selector.SelectSelectorConfig(
|
||||
options=album_options,
|
||||
multiple=True,
|
||||
mode=selector.SelectSelectorMode.DROPDOWN,
|
||||
custom_value=False,
|
||||
)
|
||||
)
|
||||
if self._immich_people:
|
||||
people_options = [
|
||||
selector.SelectOptionDict(
|
||||
value=_ALL_PEOPLE, label="Select all people"
|
||||
)
|
||||
] + [
|
||||
selector.SelectOptionDict(value=pid, label=name)
|
||||
for pid, name in self._immich_people.items()
|
||||
]
|
||||
fields[vol.Optional("people")] = selector.SelectSelector(
|
||||
selector.SelectSelectorConfig(
|
||||
options=people_options,
|
||||
multiple=True,
|
||||
mode=selector.SelectSelectorMode.DROPDOWN,
|
||||
custom_value=False,
|
||||
)
|
||||
)
|
||||
fields[vol.Optional("favorites", default=False)] = selector.BooleanSelector()
|
||||
fields[vol.Optional(CONF_IMMICH_FILTER)] = str
|
||||
fields[
|
||||
vol.Optional(CONF_IMMICH_IMAGE_SIZE, default=DEFAULT_IMMICH_IMAGE_SIZE)
|
||||
] = vol.In(IMMICH_IMAGE_SIZE_OPTIONS)
|
||||
schema = vol.Schema(fields)
|
||||
return self.async_show_form(
|
||||
step_id="immich_select", data_schema=schema, errors=errors
|
||||
step_id="immich_select",
|
||||
data_schema=_immich_select_schema(
|
||||
self._immich_albums, self._immich_people
|
||||
),
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_photoprism(
|
||||
@@ -1263,6 +1360,38 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
)
|
||||
|
||||
|
||||
class GoogleOptionsFlow(config_entries.OptionsFlow):
|
||||
"""Control anonymous Google photo metadata enrichment."""
|
||||
|
||||
async def async_step_init(self, user_input: dict[str, Any] | None = None) -> FlowResult:
|
||||
return await self.async_step_google_metadata(user_input)
|
||||
|
||||
async def async_step_google_metadata(
|
||||
self, user_input: dict[str, Any] | None = None,
|
||||
) -> FlowResult:
|
||||
defaults = {
|
||||
CONF_GOOGLE_METADATA: DEFAULT_GOOGLE_METADATA,
|
||||
CONF_GOOGLE_LOCATION: DEFAULT_GOOGLE_LOCATION,
|
||||
CONF_REVERSE_GEOCODE: DEFAULT_GOOGLE_REVERSE_GEOCODE,
|
||||
}
|
||||
if user_input is not None:
|
||||
options = {
|
||||
**self.config_entry.options,
|
||||
**{
|
||||
field: bool(user_input.get(field, self.config_entry.options.get(field, default)))
|
||||
for field, default in defaults.items()
|
||||
},
|
||||
}
|
||||
return self.async_create_entry(title="", data=options)
|
||||
return self.async_show_form(
|
||||
step_id="google_metadata",
|
||||
data_schema=vol.Schema({
|
||||
vol.Required(field, default=bool(self.config_entry.options.get(field, default))): bool
|
||||
for field, default in defaults.items()
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
class LocalFolderOptionsFlow(config_entries.OptionsFlow):
|
||||
"""Options for local-folder entries.
|
||||
|
||||
@@ -1295,6 +1424,184 @@ class LocalFolderOptionsFlow(config_entries.OptionsFlow):
|
||||
return self.async_show_form(step_id="init", data_schema=schema)
|
||||
|
||||
|
||||
class ImmichOptionsFlow(config_entries.OptionsFlow):
|
||||
"""Change an Immich entry's source without re-creating the entry.
|
||||
|
||||
The selection lives in ``entry.data`` (the coordinator reads it from
|
||||
there), so this flow writes the new selection back into the entry data;
|
||||
the update listener then reloads the entry. The entry id is unchanged,
|
||||
so entities, their settings and the hidden-photo list are kept.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._albums: dict[str, str] = {}
|
||||
self._people: dict[str, str] = {}
|
||||
self._url: str | None = None
|
||||
self._key: str | None = None
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
from . import immich as immich_api
|
||||
|
||||
data = self.config_entry.data
|
||||
client = immich_api.ImmichClient(
|
||||
self.hass, data.get(CONF_IMMICH_URL), data.get(CONF_IMMICH_API_KEY)
|
||||
)
|
||||
try:
|
||||
await client.async_validate()
|
||||
except Exception as err: # noqa: BLE001
|
||||
_LOGGER.warning(
|
||||
"Immich validation failed for %s: %s",
|
||||
client.base_url,
|
||||
_describe_error(err),
|
||||
)
|
||||
return await self.async_step_connection(
|
||||
errors={"base": _immich_validation_error(err)}
|
||||
)
|
||||
self._url = client.base_url
|
||||
self._key = data.get(CONF_IMMICH_API_KEY)
|
||||
self._albums, self._people = await _fetch_immich_choices(client)
|
||||
return await self.async_step_immich_select()
|
||||
|
||||
async def async_step_connection(
|
||||
self,
|
||||
user_input: dict[str, Any] | None = None,
|
||||
errors: dict[str, str] | None = None,
|
||||
) -> FlowResult:
|
||||
"""Fix the URL or API key when the stored ones stop working."""
|
||||
from . import immich as immich_api
|
||||
|
||||
errors = dict(errors or {})
|
||||
data = self.config_entry.data
|
||||
if user_input is not None:
|
||||
url = user_input[CONF_IMMICH_URL].strip()
|
||||
key = user_input[CONF_IMMICH_API_KEY].strip()
|
||||
client = immich_api.ImmichClient(self.hass, url, key)
|
||||
try:
|
||||
await client.async_validate()
|
||||
except Exception as err: # noqa: BLE001
|
||||
_LOGGER.warning(
|
||||
"Immich validation failed for %s: %s",
|
||||
client.base_url,
|
||||
_describe_error(err),
|
||||
)
|
||||
errors["base"] = _immich_validation_error(err)
|
||||
else:
|
||||
self._url = client.base_url
|
||||
self._key = key
|
||||
self._albums, self._people = await _fetch_immich_choices(client)
|
||||
return await self.async_step_immich_select()
|
||||
|
||||
schema = vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_IMMICH_URL, default=data.get(CONF_IMMICH_URL, "")
|
||||
): str,
|
||||
vol.Required(
|
||||
CONF_IMMICH_API_KEY, default=data.get(CONF_IMMICH_API_KEY, "")
|
||||
): str,
|
||||
}
|
||||
)
|
||||
return self.async_show_form(
|
||||
step_id="connection", data_schema=schema, errors=errors
|
||||
)
|
||||
|
||||
async def async_step_immich_select(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
entry = self.config_entry
|
||||
errors: dict[str, str] = {}
|
||||
current = _immich_current_selection(entry.data)
|
||||
for album_id in current["albums"]:
|
||||
self._albums.setdefault(album_id, f"{album_id} (unavailable)")
|
||||
for person_id in current["people"]:
|
||||
self._people.setdefault(person_id, f"{person_id} (unavailable)")
|
||||
|
||||
if user_input is not None:
|
||||
fields, errors = _parse_immich_select(
|
||||
user_input, self._albums, self._people
|
||||
)
|
||||
if not errors:
|
||||
new_data = {
|
||||
**entry.data,
|
||||
**fields,
|
||||
CONF_IMMICH_URL: self._url,
|
||||
CONF_IMMICH_API_KEY: self._key,
|
||||
}
|
||||
if not new_data[CONF_IMMICH_FILTER]:
|
||||
new_data.pop(CONF_IMMICH_FILTER)
|
||||
try:
|
||||
if _immich_source_changed(entry.data, new_data):
|
||||
await _async_clear_item_cache(self.hass, entry.entry_id)
|
||||
except Exception as err:
|
||||
_LOGGER.warning(
|
||||
"Could not clear Immich source cache for %s (%s)",
|
||||
entry.entry_id, type(err).__name__,
|
||||
)
|
||||
errors["base"] = "immich_cache_clear_failed"
|
||||
else:
|
||||
options = {
|
||||
**entry.options,
|
||||
CONF_REVERSE_GEOCODE: user_input.get(
|
||||
CONF_REVERSE_GEOCODE,
|
||||
entry.options.get(CONF_REVERSE_GEOCODE, DEFAULT_REVERSE_GEOCODE),
|
||||
),
|
||||
}
|
||||
self.hass.config_entries.async_update_entry(
|
||||
entry, data=new_data, title=fields[CONF_ALBUM_NAME], options=options
|
||||
)
|
||||
return self.async_create_entry(title="", data=options)
|
||||
|
||||
defaults = {
|
||||
CONF_ALBUM_NAME: entry.data.get(CONF_ALBUM_NAME) or entry.title,
|
||||
"albums": current["albums"],
|
||||
"people": current["people"],
|
||||
"favorites": current["favorites"],
|
||||
CONF_IMMICH_IMAGE_SIZE: entry.data.get(
|
||||
CONF_IMMICH_IMAGE_SIZE, DEFAULT_IMMICH_IMAGE_SIZE
|
||||
),
|
||||
}
|
||||
if entry.data.get(CONF_IMMICH_FILTER):
|
||||
defaults[CONF_IMMICH_FILTER] = entry.data[CONF_IMMICH_FILTER]
|
||||
schema = _immich_select_schema(self._albums, self._people, defaults).extend({
|
||||
vol.Required(CONF_REVERSE_GEOCODE, default=bool(entry.options.get(
|
||||
CONF_REVERSE_GEOCODE, DEFAULT_REVERSE_GEOCODE,
|
||||
))): bool,
|
||||
})
|
||||
return self.async_show_form(
|
||||
step_id="immich_select",
|
||||
data_schema=schema,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
|
||||
def _immich_source_changed(old: dict[str, Any], new: dict[str, Any]) -> bool:
|
||||
"""True when the set of photos an Immich entry shows may have changed."""
|
||||
keys = (
|
||||
CONF_IMMICH_URL,
|
||||
CONF_IMMICH_API_KEY,
|
||||
CONF_IMMICH_SELECTION_TYPE,
|
||||
CONF_IMMICH_SELECTION_ID,
|
||||
CONF_IMMICH_FILTER,
|
||||
CONF_IMMICH_IMAGE_SIZE,
|
||||
)
|
||||
return any(old.get(key) != new.get(key) for key in keys)
|
||||
|
||||
|
||||
async def _async_clear_item_cache(hass: Any, entry_id: str) -> None:
|
||||
"""Drop the persisted item list so a new source never shows old photos.
|
||||
|
||||
The coordinator serves this cache when a refresh fails or comes back
|
||||
empty; after a source change that would resurrect the previous album.
|
||||
Uses the same storage key as ``AlbumCoordinator._items_cache_store``.
|
||||
"""
|
||||
from homeassistant.helpers.storage import Store
|
||||
|
||||
await Store(hass, 1, f"{DOMAIN}.{entry_id}.items").async_remove()
|
||||
|
||||
|
||||
class _NoOptionsFlow(config_entries.OptionsFlow):
|
||||
"""Fallback options flow for providers that expose nothing tunable."""
|
||||
|
||||
|
||||
@@ -12,6 +12,15 @@ CONF_ALBUM_NAME = "album_name"
|
||||
CONF_LOCAL_PATH = "local_path"
|
||||
CONF_RECURSIVE = "recursive"
|
||||
CONF_IMAGE_CACHE_MB = "image_cache_mb"
|
||||
CONF_GOOGLE_METADATA = "google_metadata"
|
||||
DEFAULT_GOOGLE_METADATA = True
|
||||
CONF_GOOGLE_LOCATION = "google_location"
|
||||
DEFAULT_GOOGLE_LOCATION = False
|
||||
DEFAULT_GOOGLE_REVERSE_GEOCODE = False
|
||||
CAMERA_METADATA_FIELDS = (
|
||||
"camera_make", "camera_model", "focal_length_mm", "aperture_f_number",
|
||||
"iso", "exposure_time_seconds",
|
||||
)
|
||||
# Media Source provider: a ``media-source://...`` content id pointing at a
|
||||
# folder-like node (e.g. an Immich people/album view, or local media). The
|
||||
# coordinator browses it, collects the image children, and resolves each to
|
||||
@@ -258,6 +267,9 @@ DATE_FILTER_OFF = "off"
|
||||
DATE_FILTER_LAST_7 = "last_7_days"
|
||||
DATE_FILTER_LAST_30 = "last_30_days"
|
||||
DATE_FILTER_LAST_365 = "last_365_days"
|
||||
DATE_FILTER_CUSTOM = "custom_days"
|
||||
DEFAULT_CUSTOM_LOOKBACK_DAYS = 365
|
||||
MAX_CUSTOM_LOOKBACK_DAYS = 36500
|
||||
DATE_FILTER_THIS_MONTH = "this_month"
|
||||
DATE_FILTER_THIS_YEAR = "this_year"
|
||||
DATE_FILTER_ON_THIS_DAY = "on_this_day"
|
||||
@@ -267,6 +279,7 @@ DATE_FILTER_OPTIONS = [
|
||||
DATE_FILTER_LAST_7,
|
||||
DATE_FILTER_LAST_30,
|
||||
DATE_FILTER_LAST_365,
|
||||
DATE_FILTER_CUSTOM,
|
||||
DATE_FILTER_THIS_MONTH,
|
||||
DATE_FILTER_THIS_YEAR,
|
||||
DATE_FILTER_ON_THIS_DAY,
|
||||
@@ -293,6 +306,7 @@ DEFAULT_REFRESH_HOURS = 24
|
||||
DEFAULT_FILL_MODE = FILL_BLUR
|
||||
DEFAULT_ORIENTATION_MISMATCH_MODE = ORIENTATION_MISMATCH_PAIR
|
||||
DEFAULT_ORDER_MODE = ORDER_RANDOM
|
||||
DEFAULT_SHUFFLE_AGE_BIAS = 0
|
||||
DEFAULT_ASPECT_RATIO = "16:9"
|
||||
DEFAULT_PAIR_DIVIDER_PX = 8
|
||||
DEFAULT_PAIR_DIVIDER_COLOR = "#FFFFFF"
|
||||
|
||||
@@ -6,11 +6,13 @@ from datetime import timedelta
|
||||
from hashlib import sha256
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
import aiohttp
|
||||
import async_timeout
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -22,6 +24,12 @@ from .const import (
|
||||
PUBLICALBUM_ENDPOINT,
|
||||
CONF_PROVIDER,
|
||||
CONF_ALBUM_URL,
|
||||
CONF_GOOGLE_METADATA,
|
||||
DEFAULT_GOOGLE_METADATA,
|
||||
CONF_GOOGLE_LOCATION,
|
||||
DEFAULT_GOOGLE_LOCATION,
|
||||
DEFAULT_GOOGLE_REVERSE_GEOCODE,
|
||||
CAMERA_METADATA_FIELDS,
|
||||
CONF_LOCAL_PATH,
|
||||
CONF_MEDIA_CONTENT_ID,
|
||||
CONF_RECURSIVE,
|
||||
@@ -97,6 +105,7 @@ from .const import (
|
||||
from .store import SlideshowStore
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_GOOGLE_METADATA_INTERVAL = 1.0
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -127,12 +136,20 @@ class MediaItem:
|
||||
# Provider-specific source identifier (e.g. the Immich asset id) used by
|
||||
# background enrichment to fetch per-item metadata.
|
||||
source_id: str | None = None
|
||||
# Detected faces as normalised [x1, y1, x2, y2, weight, selected] boxes (0..1 in
|
||||
# displayed-image coordinates). Cover-mode cropping keeps as many whole
|
||||
# faces as fit. None means no face data (non-Immich source, not scanned
|
||||
# yet or the face lookup failed); an empty list means "no faces".
|
||||
faces: list[list[float | bool]] | None = None
|
||||
# True once the local-folder EXIF reader has visited this file.
|
||||
# Prevents re-reading EXIF on every coordinator refresh and lets the
|
||||
# background enrichment task skip already-processed files even after
|
||||
# an HA restart (the flag round-trips through the items cache).
|
||||
exif_scanned: bool = False
|
||||
photo_id: str | None = None
|
||||
face_scanned: bool = False
|
||||
camera_metadata: dict[str, Any] | None = None
|
||||
google_location_scanned: bool = False
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.photo_id is None:
|
||||
@@ -311,11 +328,11 @@ def _pick_timestamp_ms(d: dict[str, Any], *path: str) -> int | None:
|
||||
if not isinstance(cur, dict):
|
||||
return None
|
||||
cur = cur.get(p)
|
||||
if cur is None:
|
||||
if cur is None or isinstance(cur, bool):
|
||||
return None
|
||||
if isinstance(cur, int):
|
||||
# Heuristic: < 1e12 means seconds, otherwise milliseconds.
|
||||
return cur * 1000 if cur < 10**12 else cur
|
||||
# Infer seconds only within the plausible 1800-2100 date range.
|
||||
return cur * 1000 if -5_364_662_400 <= cur <= 4_102_444_800 else cur
|
||||
if isinstance(cur, str):
|
||||
try:
|
||||
iso = cur.replace("Z", "+00:00")
|
||||
@@ -451,6 +468,7 @@ _EXIF_GPS_LAT_REF = 1 # "N" / "S"
|
||||
_EXIF_GPS_LAT = 2 # rational tuple
|
||||
_EXIF_GPS_LON_REF = 3 # "E" / "W"
|
||||
_EXIF_GPS_LON = 4 # rational tuple
|
||||
_HEIF_EXIF_RE = re.compile(rb"Exif\x00\x00(?:MM\x00\x2a|II\x2a\x00)")
|
||||
|
||||
# Nominatim (OpenStreetMap) is free but rate-limited to 1 request/second
|
||||
# per IP and asks integrators to identify themselves with a descriptive
|
||||
@@ -777,7 +795,7 @@ def _read_exif_from_image(img: Any, out: dict[str, Any]) -> None:
|
||||
|
||||
|
||||
def _read_exif_from_bytes(
|
||||
data: bytes, mtime_fallback_ms: int | None
|
||||
data: bytes, mtime_fallback_ms: int | None, *, strict: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
"""Read EXIF metadata from already-downloaded image bytes.
|
||||
|
||||
@@ -802,11 +820,61 @@ def _read_exif_from_bytes(
|
||||
with Image.open(io.BytesIO(data)) as img:
|
||||
_read_exif_from_image(img, out)
|
||||
except Exception as err:
|
||||
if strict:
|
||||
raise
|
||||
_LOGGER.debug("EXIF: failed to read image bytes: %s", err)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def _read_gps_from_header(data: bytes) -> dict[str, float]:
|
||||
"""Read GPS from an original-image prefix; raise ``ValueError`` if it is too short to tell."""
|
||||
if data[:2] == b"\xff\xd8":
|
||||
payload = _jpeg_exif_segment(data)
|
||||
elif data[4:8] == b"ftyp":
|
||||
match = _HEIF_EXIF_RE.search(data)
|
||||
if match is None:
|
||||
raise ValueError("HEIF EXIF not found in header")
|
||||
payload = data[match.start():]
|
||||
else:
|
||||
metadata = _read_exif_from_bytes(data, None, strict=True)
|
||||
return {key: metadata[key] for key in ("latitude", "longitude") if key in metadata}
|
||||
if payload is None:
|
||||
return {}
|
||||
|
||||
from PIL import Image
|
||||
|
||||
try:
|
||||
exif = Image.Exif()
|
||||
exif.load(payload)
|
||||
gps = exif.get_ifd(_EXIF_TAG_GPS_IFD)
|
||||
except Exception:
|
||||
return {}
|
||||
lat = _gps_to_decimal(gps.get(_EXIF_GPS_LAT), gps.get(_EXIF_GPS_LAT_REF))
|
||||
lon = _gps_to_decimal(gps.get(_EXIF_GPS_LON), gps.get(_EXIF_GPS_LON_REF))
|
||||
return {} if lat is None or lon is None else {"latitude": lat, "longitude": lon}
|
||||
|
||||
|
||||
def _jpeg_exif_segment(data: bytes) -> bytes | None:
|
||||
pos = 2
|
||||
while pos + 4 <= len(data):
|
||||
if data[pos] != 0xFF:
|
||||
return None
|
||||
marker = data[pos + 1]
|
||||
if marker == 0xFF:
|
||||
pos += 1
|
||||
continue
|
||||
if marker in (0xD9, 0xDA):
|
||||
return None
|
||||
end = pos + 2 + int.from_bytes(data[pos + 2:pos + 4], "big")
|
||||
if marker == 0xE1 and data[pos + 4:pos + 10] == b"Exif\x00\x00":
|
||||
if end > len(data):
|
||||
raise ValueError("JPEG EXIF segment is truncated")
|
||||
return data[pos + 4:end]
|
||||
pos = end
|
||||
raise ValueError("JPEG header ends before image data")
|
||||
|
||||
|
||||
def _format_nominatim_location(payload: dict[str, Any]) -> str | None:
|
||||
"""Turn a Nominatim reverse-geocode response into a short label.
|
||||
|
||||
@@ -988,8 +1056,12 @@ def _merge_prior_enrichment(
|
||||
item.location = prev.location
|
||||
if prev.description and not item.description:
|
||||
item.description = prev.description
|
||||
if prev.faces is not None and item.faces is None:
|
||||
item.faces = prev.faces
|
||||
if prev.exif_scanned:
|
||||
item.exif_scanned = True
|
||||
if prev.face_scanned and prev.faces is not None:
|
||||
item.face_scanned = True
|
||||
|
||||
|
||||
class AlbumCoordinator(DataUpdateCoordinator):
|
||||
@@ -1008,6 +1080,13 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
self.store = store
|
||||
|
||||
self.provider: str = entry.data.get(CONF_PROVIDER, PROVIDER_GOOGLE_SHARED)
|
||||
self._immich_cache_source = None
|
||||
if self.provider == PROVIDER_IMMICH:
|
||||
source = [entry.data.get(key) for key in (
|
||||
CONF_IMMICH_URL, CONF_IMMICH_API_KEY, CONF_IMMICH_SELECTION_TYPE,
|
||||
CONF_IMMICH_SELECTION_ID, CONF_IMMICH_FILTER, CONF_IMMICH_IMAGE_SIZE,
|
||||
)]
|
||||
self._immich_cache_source = sha256(json.dumps(source).encode()).hexdigest()
|
||||
self.album_url: str | None = entry.data.get(CONF_ALBUM_URL)
|
||||
self.local_path: str | None = entry.data.get(CONF_LOCAL_PATH)
|
||||
self.recursive: bool = bool(entry.data.get(CONF_RECURSIVE, True))
|
||||
@@ -1106,7 +1185,29 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
raise
|
||||
|
||||
items = data.get("items") or []
|
||||
if self.provider in ENRICHING_PROVIDERS and items:
|
||||
self._apply_google_location_privacy(items)
|
||||
if (self.google_metadata_enabled or self.google_location_enabled) and items:
|
||||
prior = (self.data or {}).get("items", []) if isinstance(self.data, dict) else []
|
||||
if not self._items_cache_loaded:
|
||||
cached = await self._load_cached_items()
|
||||
prior = (cached or {}).get("items", []) + prior
|
||||
self._items_cache_loaded = True
|
||||
by_id = {item.source_id: item for item in prior if item.source_id}
|
||||
for item in items:
|
||||
previous = by_id.get(item.source_id)
|
||||
if previous is None:
|
||||
continue
|
||||
if self.google_metadata_enabled and isinstance(previous.camera_metadata, dict):
|
||||
item.camera_metadata = dict(previous.camera_metadata)
|
||||
item.description = previous.description
|
||||
if self.google_location_enabled and previous.google_location_scanned:
|
||||
item.latitude = previous.latitude
|
||||
item.longitude = previous.longitude
|
||||
item.location = previous.location
|
||||
item.google_location_scanned = True
|
||||
self._apply_google_location_privacy(items)
|
||||
self._schedule_enrichment(data)
|
||||
elif self.provider in ENRICHING_PROVIDERS and items:
|
||||
# Carry forward EXIF/geocode metadata for items we've already
|
||||
# scanned this session; new items get filled in by the
|
||||
# background worker below.
|
||||
@@ -1144,13 +1245,57 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
finally:
|
||||
self._enrichment_task = None
|
||||
|
||||
@property
|
||||
def google_metadata_enabled(self) -> bool:
|
||||
options = getattr(getattr(self, "entry", None), "options", None) or {}
|
||||
return self.provider == PROVIDER_GOOGLE_SHARED and bool(
|
||||
options.get(CONF_GOOGLE_METADATA, DEFAULT_GOOGLE_METADATA)
|
||||
)
|
||||
|
||||
@property
|
||||
def google_location_enabled(self) -> bool:
|
||||
options = getattr(getattr(self, "entry", None), "options", None) or {}
|
||||
return self.provider == PROVIDER_GOOGLE_SHARED and bool(
|
||||
options.get(CONF_GOOGLE_LOCATION, DEFAULT_GOOGLE_LOCATION)
|
||||
)
|
||||
|
||||
@property
|
||||
def google_reverse_geocode_enabled(self) -> bool:
|
||||
options = getattr(getattr(self, "entry", None), "options", None) or {}
|
||||
return self.google_location_enabled and bool(
|
||||
options.get(CONF_REVERSE_GEOCODE, DEFAULT_GOOGLE_REVERSE_GEOCODE)
|
||||
)
|
||||
|
||||
def _apply_google_location_privacy(self, items: list[MediaItem]) -> None:
|
||||
if getattr(self, "provider", None) != PROVIDER_GOOGLE_SHARED:
|
||||
return
|
||||
for item in items:
|
||||
if not self.google_location_enabled:
|
||||
item.latitude = None
|
||||
item.longitude = None
|
||||
item.location = None
|
||||
item.google_location_scanned = False
|
||||
elif not self.google_reverse_geocode_enabled:
|
||||
item.location = None
|
||||
|
||||
def _needs_enrichment(self, item: MediaItem) -> bool:
|
||||
if self.provider == PROVIDER_GOOGLE_SHARED:
|
||||
return (
|
||||
self.google_metadata_enabled and bool(item.source_id) and item.camera_metadata is None
|
||||
) or (self.google_location_enabled and not item.google_location_scanned)
|
||||
if not item.exif_scanned:
|
||||
return True
|
||||
return self.provider == PROVIDER_IMMICH and not item.face_scanned
|
||||
|
||||
def _schedule_enrichment(self, data: dict[str, Any]) -> None:
|
||||
"""Kick off the background EXIF + geocode worker if there's work."""
|
||||
items: list[MediaItem] = data.get("items") or []
|
||||
unscanned = [it for it in items if not it.exif_scanned]
|
||||
unscanned = [it for it in items if self._needs_enrichment(it)]
|
||||
# Providers that decrypt/return GPS inline (Ente) have nothing to scan
|
||||
# but still need the coordinates turned into a place label.
|
||||
needs_geocode = any(
|
||||
needs_geocode = (
|
||||
self.provider != PROVIDER_GOOGLE_SHARED or self.google_reverse_geocode_enabled
|
||||
) and any(
|
||||
it.latitude is not None and it.longitude is not None and not it.location
|
||||
for it in items
|
||||
)
|
||||
@@ -1174,7 +1319,8 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
"geocode_done": 0,
|
||||
}
|
||||
self._enrichment_task = self.hass.async_create_background_task(
|
||||
self._enrich_items_background(data),
|
||||
self._enrich_google_items_background(data) if self.provider == PROVIDER_GOOGLE_SHARED
|
||||
else self._enrich_items_background(data),
|
||||
name=f"album_slideshow_enrich_{self.entry.entry_id}",
|
||||
)
|
||||
|
||||
@@ -1187,6 +1333,10 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
if not isinstance(payload, dict):
|
||||
return None
|
||||
|
||||
cached_source = payload.get("immich_source")
|
||||
if cached_source is not None and cached_source != getattr(self, "_immich_cache_source", None):
|
||||
return None
|
||||
|
||||
raw_items = payload.get("items")
|
||||
if not isinstance(raw_items, list):
|
||||
return None
|
||||
@@ -1210,12 +1360,21 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
location=raw.get("location"),
|
||||
description=raw.get("description"),
|
||||
source_id=raw.get("source_id"),
|
||||
faces=raw.get("faces"),
|
||||
exif_scanned=bool(raw.get("exif_scanned", False)),
|
||||
photo_id=raw.get("photo_id"),
|
||||
face_scanned=bool(raw.get("face_scanned", False))
|
||||
and isinstance(raw.get("faces"), list),
|
||||
camera_metadata={
|
||||
key: value for key, value in raw["camera_metadata"].items()
|
||||
if key in CAMERA_METADATA_FIELDS
|
||||
} if isinstance(raw.get("camera_metadata"), dict) else None,
|
||||
google_location_scanned=bool(raw.get("google_location_scanned", False)),
|
||||
))
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
self._apply_google_location_privacy(items)
|
||||
return {
|
||||
"title": payload.get("title"),
|
||||
"items": items,
|
||||
@@ -1223,6 +1382,7 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
|
||||
async def _save_cached_items(self, data: dict[str, Any]) -> None:
|
||||
items = data.get("items") or []
|
||||
self._apply_google_location_privacy(items)
|
||||
# Local-folder URLs are absolute paths on the host; persisting them
|
||||
# is fine but they don't survive a host reformat. Persist anyway,
|
||||
# the URL check at load time will skip any stale entries.
|
||||
@@ -1243,12 +1403,18 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
"location": it.location,
|
||||
"description": it.description,
|
||||
"source_id": it.source_id,
|
||||
"faces": it.faces,
|
||||
"exif_scanned": it.exif_scanned,
|
||||
"photo_id": it.photo_id,
|
||||
"face_scanned": it.face_scanned,
|
||||
"camera_metadata": it.camera_metadata,
|
||||
"google_location_scanned": it.google_location_scanned,
|
||||
}
|
||||
for it in items
|
||||
],
|
||||
}
|
||||
if source := getattr(self, "_immich_cache_source", None):
|
||||
payload["immich_source"] = source
|
||||
try:
|
||||
await self._items_cache_store.async_save(payload)
|
||||
except Exception as err: # pragma: no cover - storage layer
|
||||
@@ -2168,7 +2334,7 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
return None, {}
|
||||
|
||||
async def _enrich_immich_item(self, item: MediaItem) -> None:
|
||||
"""Fetch one Immich asset's detail and fill location/description."""
|
||||
"""Fetch Immich metadata and the face boxes used for cropping."""
|
||||
from . import immich as immich_api
|
||||
|
||||
url = self.entry.data.get(CONF_IMMICH_URL)
|
||||
@@ -2177,13 +2343,36 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
item.exif_scanned = True
|
||||
return
|
||||
client = immich_api.ImmichClient(self.hass, url, api_key)
|
||||
try:
|
||||
asset = await client.async_get_asset(item.source_id)
|
||||
except Exception as err:
|
||||
_LOGGER.debug("Immich: failed to fetch asset %s: %s", item.source_id, err)
|
||||
item.exif_scanned = True
|
||||
return
|
||||
info = immich_api.parse_asset_exif(asset)
|
||||
person_ids = immich_api.selected_person_ids(
|
||||
self.entry.data.get(CONF_IMMICH_SELECTION_TYPE),
|
||||
self.entry.data.get(CONF_IMMICH_SELECTION_ID),
|
||||
)
|
||||
|
||||
requests = {}
|
||||
needs_faces = not item.face_scanned
|
||||
if not item.exif_scanned or needs_faces:
|
||||
requests["asset"] = client.async_get_asset(item.source_id)
|
||||
if needs_faces:
|
||||
requests["faces"] = client.async_get_faces(item.source_id)
|
||||
results = await asyncio.gather(*requests.values(), return_exceptions=True)
|
||||
asset = None
|
||||
|
||||
for kind, result in zip(requests, results):
|
||||
if isinstance(result, asyncio.CancelledError):
|
||||
raise result
|
||||
if isinstance(result, Exception):
|
||||
_LOGGER.debug(
|
||||
"Immich: failed to fetch %s for asset %s: %s",
|
||||
kind,
|
||||
item.source_id,
|
||||
result,
|
||||
)
|
||||
continue
|
||||
if kind == "asset":
|
||||
if not isinstance(result, dict):
|
||||
continue
|
||||
asset = result
|
||||
info = immich_api.parse_asset_exif(result)
|
||||
if "captured_at" in info:
|
||||
item.captured_at = info["captured_at"]
|
||||
if "latitude" in info and "longitude" in info:
|
||||
@@ -2194,6 +2383,124 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
if "description" in info:
|
||||
item.description = info["description"]
|
||||
item.exif_scanned = True
|
||||
else:
|
||||
if asset is None:
|
||||
continue
|
||||
edits = []
|
||||
if asset.get("isEdited"):
|
||||
try:
|
||||
edits = await client.async_get_asset_edits(item.source_id)
|
||||
if not edits:
|
||||
continue
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception:
|
||||
_LOGGER.debug("Immich: edit metadata unavailable for asset %s", item.source_id)
|
||||
continue
|
||||
try:
|
||||
boxes = immich_api.parse_face_boxes(
|
||||
result, person_ids, edits=edits,
|
||||
original_size=immich_api.original_image_size(asset),
|
||||
)
|
||||
except (TypeError, ValueError):
|
||||
_LOGGER.debug("Immich: unsupported face geometry for asset %s", item.source_id)
|
||||
continue
|
||||
item.faces = boxes
|
||||
item.face_scanned = True
|
||||
|
||||
async def _enrich_google_items_background(self, data: dict[str, Any]) -> None:
|
||||
"""Cache optional Google metadata without delaying or failing the playlist."""
|
||||
from . import google_scraper
|
||||
|
||||
jobs = []
|
||||
for item in data.get("items", []):
|
||||
if self.google_metadata_enabled and item.source_id and item.camera_metadata is None:
|
||||
jobs.append((item, False))
|
||||
if self.google_location_enabled and not item.google_location_scanned:
|
||||
jobs.append((item, True))
|
||||
if not jobs:
|
||||
try:
|
||||
if self.google_reverse_geocode_enabled:
|
||||
await self._geocode_items_background(data)
|
||||
finally:
|
||||
self._enrich_progress["phase"] = "done"
|
||||
return
|
||||
failures = 0
|
||||
completed = 0
|
||||
next_request = 0.0
|
||||
loop = asyncio.get_running_loop()
|
||||
try:
|
||||
async with aiohttp.ClientSession(
|
||||
cookie_jar=aiohttp.DummyCookieJar(), trust_env=False,
|
||||
) as session:
|
||||
keys = None
|
||||
for item, location_job in jobs:
|
||||
delay = next_request - loop.time()
|
||||
if delay > 0:
|
||||
await asyncio.sleep(delay)
|
||||
try:
|
||||
if location_job:
|
||||
header = await google_scraper.fetch_photo_location_header(session, item.url)
|
||||
try:
|
||||
metadata = await self.hass.async_add_executor_job(
|
||||
_read_gps_from_header, header,
|
||||
)
|
||||
except Exception as err:
|
||||
# A photo that cannot be parsed must not stop the scan for the rest.
|
||||
_LOGGER.debug("Google photo GPS unreadable (%s)", type(err).__name__)
|
||||
continue
|
||||
latitude = metadata.get("latitude")
|
||||
longitude = metadata.get("longitude")
|
||||
if (
|
||||
all(type(value) in (int, float) and math.isfinite(value)
|
||||
for value in (latitude, longitude))
|
||||
and -90 <= latitude <= 90 and -180 <= longitude <= 180
|
||||
and (abs(latitude) > 1e-6 or abs(longitude) > 1e-6)
|
||||
):
|
||||
item.latitude, item.longitude = latitude, longitude
|
||||
else:
|
||||
item.latitude = item.longitude = None
|
||||
item.location = None
|
||||
item.google_location_scanned = True
|
||||
else:
|
||||
if keys is None:
|
||||
keys = await google_scraper.fetch_metadata_keys(session, self.album_url)
|
||||
metadata = await google_scraper.fetch_photo_metadata(
|
||||
session, keys, item.source_id,
|
||||
)
|
||||
item.description = metadata.pop("description", None)
|
||||
item.camera_metadata = metadata
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception as err:
|
||||
failures += 1
|
||||
_LOGGER.debug(
|
||||
"Google photo metadata unavailable (%s, status=%s)",
|
||||
type(err).__name__, getattr(err, "status", None),
|
||||
)
|
||||
if failures >= 3 or getattr(err, "status", None) in (401, 403, 429):
|
||||
break
|
||||
else:
|
||||
failures = 0
|
||||
completed += 1
|
||||
if not self._needs_enrichment(item):
|
||||
self._enrich_progress["exif_done"] += 1
|
||||
if completed == 1 or completed % 10 == 0:
|
||||
await self._save_cached_items(data)
|
||||
self.async_set_updated_data(data)
|
||||
finally:
|
||||
next_request = loop.time() + _GOOGLE_METADATA_INTERVAL
|
||||
if self.google_reverse_geocode_enabled:
|
||||
await self._geocode_items_background(data)
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
except Exception as err:
|
||||
_LOGGER.debug("Google photo enrichment stopped (%s)", type(err).__name__)
|
||||
finally:
|
||||
self._enrich_progress["phase"] = "done"
|
||||
await self._save_cached_items(data)
|
||||
if completed:
|
||||
self.async_set_updated_data(data)
|
||||
|
||||
async def _enrich_items_background(self, data: dict[str, Any]) -> None:
|
||||
"""Read EXIF for unscanned local files, then reverse-geocode.
|
||||
@@ -2213,7 +2520,7 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
scanned_since_save = 0
|
||||
try:
|
||||
for idx, item in enumerate(items):
|
||||
if item.exif_scanned:
|
||||
if not self._needs_enrichment(item):
|
||||
# Fast path: yield occasionally so we don't starve
|
||||
# the event loop when (re-)visiting a long list of
|
||||
# already-processed items.
|
||||
@@ -2318,6 +2625,8 @@ class AlbumCoordinator(DataUpdateCoordinator):
|
||||
the GPS coordinates themselves. Successful labels are written
|
||||
back into the items in-place and persisted via the items cache.
|
||||
"""
|
||||
if getattr(self, "provider", None) == PROVIDER_GOOGLE_SHARED and not self.google_reverse_geocode_enabled:
|
||||
return
|
||||
if not bool(
|
||||
self.entry.options.get(CONF_REVERSE_GEOCODE, DEFAULT_REVERSE_GEOCODE)
|
||||
if hasattr(self.entry, "options") and self.entry.options is not None
|
||||
|
||||
@@ -26,9 +26,10 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import re
|
||||
from typing import Any
|
||||
from urllib.parse import quote
|
||||
from urllib.parse import quote, urlsplit, urlunsplit
|
||||
|
||||
from .coordinator import MediaItem
|
||||
|
||||
@@ -61,6 +62,7 @@ _SIZE_SUFFIX_RE = re.compile(r"=[wh]\d+(?:-[a-z0-9]+)*$", re.IGNORECASE)
|
||||
|
||||
_VIDEO_DURATION_KEY = 76647426 # presence indicates a video; we skip those
|
||||
_LIVEPHOTO_KEY = 146008172
|
||||
_LOCATION_HEADER_BYTES = 256 * 1024
|
||||
|
||||
|
||||
class _AlbumKeys:
|
||||
@@ -129,6 +131,146 @@ async def fetch_album(
|
||||
return title, items, video_keys
|
||||
|
||||
|
||||
async def fetch_metadata_keys(session, share_url: str, *, timeout: float = 15.0):
|
||||
"""Read public share keys for optional enrichment, without changing pagination."""
|
||||
async with session.get(
|
||||
share_url, headers={"User-Agent": _BROWSER_UA}, timeout=timeout,
|
||||
allow_redirects=True,
|
||||
) as response:
|
||||
html = await _read_metadata_body(response, 4 * 1024 * 1024)
|
||||
keys = _extract_keys(html)
|
||||
if keys is None:
|
||||
raise ValueError("Public Google album metadata keys unavailable")
|
||||
return keys
|
||||
|
||||
|
||||
async def fetch_photo_location_header(
|
||||
session, image_url: str, *, timeout: float = 15.0,
|
||||
) -> bytes:
|
||||
"""Read a bounded original-image prefix for explicitly enabled GPS extraction."""
|
||||
parsed = urlsplit(image_url)
|
||||
if (
|
||||
parsed.scheme != "https"
|
||||
or not (parsed.hostname or "").endswith(".googleusercontent.com")
|
||||
or parsed.port not in (None, 443)
|
||||
or parsed.username is not None
|
||||
or parsed.password is not None
|
||||
):
|
||||
raise ValueError("Unexpected Google image URL")
|
||||
original = urlunsplit(parsed._replace(
|
||||
path=_SIZE_SUFFIX_RE.sub("", parsed.path) + "=d", fragment="",
|
||||
))
|
||||
async with session.get(
|
||||
original, headers={"Range": f"bytes=0-{_LOCATION_HEADER_BYTES - 1}"},
|
||||
timeout=timeout, allow_redirects=False,
|
||||
) as response:
|
||||
response.raise_for_status()
|
||||
if response.status not in (200, 206):
|
||||
raise ValueError("Unexpected Google image response")
|
||||
content_type = response.headers.get("Content-Type", "").split(";", 1)[0].lower()
|
||||
if not content_type.startswith("image/") and content_type != "application/octet-stream":
|
||||
raise ValueError("Google image response is not an image")
|
||||
if response.status == 206 and not re.fullmatch(
|
||||
r"bytes 0-\d+/(?:\d+|\*)", response.headers.get("Content-Range", ""),
|
||||
):
|
||||
raise ValueError("Unexpected Google image byte range")
|
||||
content = bytearray()
|
||||
async for chunk in response.content.iter_chunked(32768):
|
||||
content.extend(chunk[:_LOCATION_HEADER_BYTES - len(content)])
|
||||
if len(content) >= _LOCATION_HEADER_BYTES:
|
||||
break
|
||||
return bytes(content)
|
||||
|
||||
|
||||
async def fetch_photo_metadata(
|
||||
session, keys: _AlbumKeys, media_key: str, *, timeout: float = 15.0,
|
||||
) -> dict[str, Any]:
|
||||
"""Read camera metadata and description from Google's public photo endpoint."""
|
||||
payload = [media_key, None, keys.auth_key, None, keys.album_key]
|
||||
envelope = json.dumps([[["VrseUb", json.dumps(payload), None, "generic"]]])
|
||||
endpoint = "https://photos.google.com/u/0/_/PhotosUi/data/batchexecute"
|
||||
headers = {
|
||||
"User-Agent": _BROWSER_UA,
|
||||
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
|
||||
"Origin": "https://photos.google.com",
|
||||
"Referer": f"https://photos.google.com/share/{keys.album_key}?key={keys.auth_key}",
|
||||
}
|
||||
async with session.post(
|
||||
endpoint, params={"rpcids": "VrseUb", "source-path": f"/share/{keys.album_key}"},
|
||||
headers=headers, data=f"f.req={quote(envelope)}", timeout=timeout,
|
||||
allow_redirects=False,
|
||||
) as response:
|
||||
body = await _read_metadata_body(response, 1024 * 1024)
|
||||
for line in body.splitlines():
|
||||
try:
|
||||
entries = json.loads(line)
|
||||
except ValueError:
|
||||
continue
|
||||
if not isinstance(entries, list):
|
||||
continue
|
||||
for entry in entries:
|
||||
if (
|
||||
isinstance(entry, list) and len(entry) >= 3
|
||||
and entry[:2] == ["wrb.fr", "VrseUb"] and isinstance(entry[2], str)
|
||||
):
|
||||
detail = json.loads(entry[2])
|
||||
if _metadata_value(detail, 0, 0) != media_key:
|
||||
raise ValueError("Google photo metadata identity mismatch")
|
||||
return parse_photo_metadata(detail)
|
||||
raise ValueError("Google photo metadata response unavailable")
|
||||
|
||||
|
||||
async def _read_metadata_body(response, limit: int) -> str:
|
||||
response.raise_for_status()
|
||||
if response.status != 200:
|
||||
raise ValueError("Unexpected Google metadata response")
|
||||
chunks = []
|
||||
length = 0
|
||||
async for chunk in response.content.iter_chunked(65536):
|
||||
length += len(chunk)
|
||||
if length > limit:
|
||||
raise ValueError("Google metadata response exceeds size limit")
|
||||
chunks.append(chunk)
|
||||
return b"".join(chunks).decode("utf-8")
|
||||
|
||||
|
||||
def _metadata_value(value: Any, *indices: int) -> Any:
|
||||
for index in indices:
|
||||
if not isinstance(value, list) or index >= len(value):
|
||||
return None
|
||||
value = value[index]
|
||||
return value
|
||||
|
||||
|
||||
def parse_photo_metadata(detail: Any) -> dict[str, Any]:
|
||||
"""Extract the camera and description fields verified in public responses."""
|
||||
photo = _metadata_value(detail, 0)
|
||||
if not isinstance(photo, list):
|
||||
raise ValueError("Invalid Google photo metadata")
|
||||
camera = _metadata_value(detail, 0, 1, 8, 4)
|
||||
result: dict[str, Any] = {}
|
||||
for field, index in (("camera_make", 0), ("camera_model", 1)):
|
||||
value = _metadata_value(camera, index)
|
||||
if isinstance(value, str) and value.strip():
|
||||
result[field] = value.strip()[:128]
|
||||
for field, index in (
|
||||
("focal_length_mm", 3), ("aperture_f_number", 4),
|
||||
("iso", 5), ("exposure_time_seconds", 6),
|
||||
):
|
||||
value = _metadata_value(camera, index)
|
||||
if type(value) in (int, float) and math.isfinite(value) and value > 0:
|
||||
if field != "iso" or float(value).is_integer():
|
||||
result[field] = int(value) if field == "iso" else float(value)
|
||||
for section in photo:
|
||||
if not isinstance(section, dict):
|
||||
continue
|
||||
description = _metadata_value(section.get("396644657", section.get(396644657)), 0)
|
||||
if isinstance(description, str) and description.strip():
|
||||
result["description"] = description.strip()
|
||||
break
|
||||
return result
|
||||
|
||||
|
||||
# -- Internals ---------------------------------------------------------------
|
||||
|
||||
async def _fetch_album_keys(
|
||||
@@ -398,13 +540,13 @@ def _parse_album_item(raw: Any) -> MediaItem | None:
|
||||
)
|
||||
|
||||
|
||||
# Plausible epoch-ms range: 2000-01-01 to 2100-01-01.
|
||||
_MIN_TS_MS = 946_684_800_000
|
||||
# Plausible epoch-ms range: 1800-01-01 to 2100-01-01.
|
||||
_MIN_TS_MS = -5_364_662_400_000
|
||||
_MAX_TS_MS = 4_102_444_800_000
|
||||
|
||||
|
||||
def _looks_like_timestamp_ms(value: Any) -> bool:
|
||||
return isinstance(value, int) and _MIN_TS_MS <= value <= _MAX_TS_MS
|
||||
return type(value) is int and _MIN_TS_MS <= value <= _MAX_TS_MS
|
||||
|
||||
|
||||
# -- AF block parsing (for the initial 300 items embedded in the HTML) ------
|
||||
|
||||
@@ -2,8 +2,10 @@ from __future__ import annotations
|
||||
|
||||
import io
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import NamedTuple
|
||||
|
||||
from PIL import Image, ImageColor, ImageFilter, ImageOps
|
||||
from PIL import Image, ImageColor, ImageDraw, ImageFilter, ImageFont, ImageOps
|
||||
|
||||
from .coordinator import MediaItem
|
||||
|
||||
@@ -14,6 +16,42 @@ FILL_COVER = "cover"
|
||||
FILL_CONTAIN = "contain"
|
||||
FILL_BLUR = "blur"
|
||||
|
||||
class FaceBox(NamedTuple):
|
||||
left: float
|
||||
top: float
|
||||
right: float
|
||||
bottom: float
|
||||
weight: float
|
||||
selected: bool = False
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CropHints:
|
||||
"""Per-photo inputs for face-aware cropping and the debug overlay.
|
||||
|
||||
``faces`` is None when no face data is known for the photo (not an
|
||||
Immich source, or not scanned yet) and an empty tuple when the photo
|
||||
was scanned and has no faces.
|
||||
"""
|
||||
|
||||
faces: tuple[FaceBox, ...] | None = None
|
||||
debug: bool = False
|
||||
name: str | None = None
|
||||
|
||||
|
||||
# Padding around a face box, as a fraction of the face's own size, so the
|
||||
# crop keeps the whole head rather than just the tight face box.
|
||||
_FACE_PAD_SIDE = 0.3
|
||||
_FACE_PAD_TOP = 0.6
|
||||
_FACE_PAD_BOTTOM = 0.3
|
||||
# A face that is cut by the crop edge counts this much worse than one that is
|
||||
# left out entirely: half a face looks like a mistake, a missing face doesn't.
|
||||
_PARTIAL_PENALTY = 1.5
|
||||
|
||||
FACE_KEPT = "kept"
|
||||
FACE_CUT = "cut"
|
||||
FACE_DROPPED = "dropped"
|
||||
|
||||
# Absolute pixel ceiling. A 20000x20000 JPEG decodes to ~1.2 GB of RGB; Pillow
|
||||
# raises DecompressionBombError above MAX_IMAGE_PIXELS. We set this high enough
|
||||
# that 4K+ sources still decode, but reject anything absurd to protect
|
||||
@@ -120,13 +158,20 @@ def resolve_output_size(
|
||||
return (width, height)
|
||||
|
||||
|
||||
def render_image(img: Image.Image, fill_mode: str, width: int, height: int) -> Image.Image:
|
||||
def render_image(
|
||||
img: Image.Image,
|
||||
fill_mode: str,
|
||||
width: int,
|
||||
height: int,
|
||||
hints: CropHints | None = None,
|
||||
) -> Image.Image:
|
||||
"""Render img into a (width x height) canvas using the given fill mode."""
|
||||
hints = hints or CropHints()
|
||||
if fill_mode == FILL_CONTAIN:
|
||||
return _resize_contain(img, width, height)
|
||||
return _resize_contain(img, width, height, hints=hints)
|
||||
if fill_mode == FILL_BLUR:
|
||||
return _blur_fill(img, width, height)
|
||||
return _resize_cover(img, width, height)
|
||||
return _blur_fill(img, width, height, hints=hints)
|
||||
return _resize_cover(img, width, height, hints)
|
||||
|
||||
|
||||
def pair_images(
|
||||
@@ -139,6 +184,8 @@ def pair_images(
|
||||
divider: int,
|
||||
divider_fill: tuple[int, int, int] | tuple[int, int, int, int],
|
||||
transparent_divider: bool,
|
||||
hints1: CropHints | None = None,
|
||||
hints2: CropHints | None = None,
|
||||
) -> Image.Image:
|
||||
canvas_mode = "RGBA" if transparent_divider else "RGB"
|
||||
canvas = Image.new(canvas_mode, (target_w, target_h), divider_fill)
|
||||
@@ -146,8 +193,8 @@ def pair_images(
|
||||
if portrait_canvas:
|
||||
top_h = max(1, (target_h - divider) // 2)
|
||||
bottom_h = max(1, target_h - divider - top_h)
|
||||
top_img = render_image(img1, fill_mode, target_w, top_h)
|
||||
bottom_img = render_image(img2, fill_mode, target_w, bottom_h)
|
||||
top_img = render_image(img1, fill_mode, target_w, top_h, hints1)
|
||||
bottom_img = render_image(img2, fill_mode, target_w, bottom_h, hints2)
|
||||
canvas.paste(top_img.convert(canvas_mode), (0, 0))
|
||||
canvas.paste(bottom_img.convert(canvas_mode), (0, top_h + divider))
|
||||
safe_close(top_img)
|
||||
@@ -156,8 +203,8 @@ def pair_images(
|
||||
|
||||
left_w = max(1, (target_w - divider) // 2)
|
||||
right_w = max(1, target_w - divider - left_w)
|
||||
left_img = render_image(img1, fill_mode, left_w, target_h)
|
||||
right_img = render_image(img2, fill_mode, right_w, target_h)
|
||||
left_img = render_image(img1, fill_mode, left_w, target_h, hints1)
|
||||
right_img = render_image(img2, fill_mode, right_w, target_h, hints2)
|
||||
canvas.paste(left_img.convert(canvas_mode), (0, 0))
|
||||
canvas.paste(right_img.convert(canvas_mode), (left_w + divider, 0))
|
||||
safe_close(left_img)
|
||||
@@ -252,7 +299,131 @@ def _parse_aspect_ratio(ratio: str) -> tuple[int, int]:
|
||||
return (16, 9)
|
||||
|
||||
|
||||
def _resize_cover(img: Image.Image, target_w: int, target_h: int) -> Image.Image:
|
||||
def _padded_face(face: FaceBox) -> tuple[float, float, float, float]:
|
||||
x1, y1, x2, y2 = face[:4]
|
||||
fw, fh = x2 - x1, y2 - y1
|
||||
return (
|
||||
max(0.0, x1 - fw * _FACE_PAD_SIDE),
|
||||
max(0.0, y1 - fh * _FACE_PAD_TOP),
|
||||
min(1.0, x2 + fw * _FACE_PAD_SIDE),
|
||||
min(1.0, y2 + fh * _FACE_PAD_BOTTOM),
|
||||
)
|
||||
|
||||
|
||||
def _span_status(a: float, b: float, offset: float, window: float) -> str:
|
||||
# One pixel of slack absorbs rounding when the image is scaled to fit
|
||||
# an axis exactly.
|
||||
if a >= offset - 1 and b <= offset + window + 1:
|
||||
return FACE_KEPT
|
||||
if b <= offset or a >= offset + window:
|
||||
return FACE_DROPPED
|
||||
return FACE_CUT
|
||||
|
||||
|
||||
def choose_crop_offset(
|
||||
spans: list[tuple[float, float, float]],
|
||||
src_len: float,
|
||||
window: float,
|
||||
*,
|
||||
selected: list[bool] | None = None,
|
||||
padded_spans: list[tuple[float, float]] | None = None,
|
||||
) -> float:
|
||||
"""Pick where a crop window of ``window`` px starts along one axis.
|
||||
|
||||
``spans`` are unpadded ``(start, end, weight)`` face intervals. Selected
|
||||
people rank ahead of all bystanders. Keep whole faces where possible,
|
||||
retain visible face area otherwise, and use padding as a preference.
|
||||
"""
|
||||
max_offset = max(0.0, src_len - window)
|
||||
centred = max_offset / 2
|
||||
if max_offset <= 0 or not spans:
|
||||
return centred
|
||||
|
||||
def clamp(value: float) -> float:
|
||||
return max(0.0, min(max_offset, value))
|
||||
|
||||
priorities = selected if selected is not None else [False] * len(spans)
|
||||
padding = padded_spans if padded_spans is not None else [span[:2] for span in spans]
|
||||
|
||||
def visible_fraction(start: float, end: float, offset: float) -> float:
|
||||
overlap = max(0.0, min(end, offset + window) - max(start, offset))
|
||||
return overlap / (end - start)
|
||||
|
||||
def score(offset: float) -> tuple[float, ...]:
|
||||
tiers = []
|
||||
for priority in (True, False):
|
||||
kept_weight = cut_weight = visible_weight = 0.0
|
||||
for (start, end, weight), is_selected in zip(spans, priorities):
|
||||
if is_selected != priority:
|
||||
continue
|
||||
state = _span_status(start, end, offset, window)
|
||||
if state == FACE_KEPT:
|
||||
kept_weight += weight
|
||||
elif state == FACE_CUT:
|
||||
cut_weight += weight
|
||||
visible_weight += weight * visible_fraction(start, end, offset)
|
||||
tiers.extend((
|
||||
kept_weight,
|
||||
-_PARTIAL_PENALTY * cut_weight if kept_weight else visible_weight,
|
||||
))
|
||||
tiers.append(sum(
|
||||
span[2] * visible_fraction(start, end, offset)
|
||||
for span, (start, end) in zip(spans, padding)
|
||||
))
|
||||
return tuple(round(value, 12) for value in tiers)
|
||||
|
||||
candidates = {0.0, centred, max_offset}
|
||||
for start, end in [span[:2] for span in spans] + padding:
|
||||
for offset in (start, end - window, end, start - window, (start + end - window) / 2):
|
||||
candidates.add(clamp(offset))
|
||||
best = max(candidates, key=lambda offset: (score(offset), -abs(offset - centred), -offset))
|
||||
|
||||
kept = [
|
||||
index for index, (start, end, _weight) in enumerate(spans)
|
||||
if _span_status(start, end, best, window) == FACE_KEPT
|
||||
]
|
||||
if not kept:
|
||||
return best
|
||||
group_start = min(padding[index][0] for index in kept)
|
||||
group_end = max(padding[index][1] for index in kept)
|
||||
if group_end - group_start > window:
|
||||
group_start = min(spans[index][0] for index in kept)
|
||||
group_end = max(spans[index][1] for index in kept)
|
||||
target = clamp((group_start + group_end - window) / 2)
|
||||
candidates.add(target)
|
||||
return max(candidates, key=lambda offset: (score(offset), -abs(offset - target), -offset))
|
||||
|
||||
|
||||
def _face_statuses(
|
||||
faces: tuple[FaceBox, ...],
|
||||
new_w: int,
|
||||
new_h: int,
|
||||
left: int,
|
||||
top: int,
|
||||
target_w: int,
|
||||
target_h: int,
|
||||
) -> list[str]:
|
||||
statuses = []
|
||||
for face in faces:
|
||||
px1, py1, px2, py2 = face[:4]
|
||||
x = _span_status(px1 * new_w, px2 * new_w, left, target_w)
|
||||
y = _span_status(py1 * new_h, py2 * new_h, top, target_h)
|
||||
if FACE_DROPPED in (x, y):
|
||||
statuses.append(FACE_DROPPED)
|
||||
elif FACE_CUT in (x, y):
|
||||
statuses.append(FACE_CUT)
|
||||
else:
|
||||
statuses.append(FACE_KEPT)
|
||||
return statuses
|
||||
|
||||
|
||||
def _resize_cover(
|
||||
img: Image.Image,
|
||||
target_w: int,
|
||||
target_h: int,
|
||||
hints: CropHints | None = None,
|
||||
) -> Image.Image:
|
||||
hints = hints or CropHints()
|
||||
src_w, src_h = img.size
|
||||
if src_w <= 0 or src_h <= 0:
|
||||
return img.resize((target_w, target_h))
|
||||
@@ -260,15 +431,137 @@ def _resize_cover(img: Image.Image, target_w: int, target_h: int) -> Image.Image
|
||||
new_w = max(1, int(round(src_w * scale)))
|
||||
new_h = max(1, int(round(src_h * scale)))
|
||||
resized = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
|
||||
left = max(0, int(round((new_w - target_w) / 2)))
|
||||
top = max(0, int(round((new_h - target_h) / 2)))
|
||||
|
||||
faces = tuple(FaceBox(*face) for face in hints.faces or ())
|
||||
padded = [_padded_face(face) for face in faces]
|
||||
left = choose_crop_offset(
|
||||
[(face.left * new_w, face.right * new_w, face.weight) for face in faces],
|
||||
new_w,
|
||||
target_w,
|
||||
selected=[face.selected for face in faces],
|
||||
padded_spans=[(box[0] * new_w, box[2] * new_w) for box in padded],
|
||||
)
|
||||
top = choose_crop_offset(
|
||||
[(face.top * new_h, face.bottom * new_h, face.weight) for face in faces],
|
||||
new_h,
|
||||
target_h,
|
||||
selected=[face.selected for face in faces],
|
||||
padded_spans=[(box[1] * new_h, box[3] * new_h) for box in padded],
|
||||
)
|
||||
left = max(0, min(max(0, new_w - target_w), int(round(left))))
|
||||
top = max(0, min(max(0, new_h - target_h), int(round(top))))
|
||||
|
||||
statuses = _face_statuses(faces, new_w, new_h, left, top, target_w, target_h)
|
||||
summary = _face_summary(hints.faces, statuses)
|
||||
if faces:
|
||||
_LOGGER.debug("Crop %s: %s", hints.name or "photo", summary)
|
||||
if hints.debug:
|
||||
_draw_debug_marks(resized, faces, statuses)
|
||||
|
||||
cropped = resized.crop((left, top, left + target_w, top + target_h))
|
||||
if cropped is not resized:
|
||||
safe_close(resized)
|
||||
if hints.debug:
|
||||
_draw_offscreen_centre(cropped, new_w / 2 - left, new_h / 2 - top)
|
||||
_draw_debug_label(cropped, summary)
|
||||
return cropped
|
||||
|
||||
|
||||
def _resize_contain(img: Image.Image, target_w: int, target_h: int, bg=(0, 0, 0)) -> Image.Image:
|
||||
def _face_summary(faces: tuple[FaceBox, ...] | None, statuses: list[str]) -> str:
|
||||
if faces is None:
|
||||
return "no face data"
|
||||
return (
|
||||
f"faces {len(statuses)} · kept {statuses.count(FACE_KEPT)} · "
|
||||
f"cut {statuses.count(FACE_CUT)} · dropped {statuses.count(FACE_DROPPED)}"
|
||||
)
|
||||
|
||||
|
||||
_DEBUG_COLORS = {
|
||||
FACE_KEPT: (0, 220, 0),
|
||||
FACE_CUT: (255, 40, 40),
|
||||
FACE_DROPPED: (160, 160, 160),
|
||||
}
|
||||
_CROSSHAIR_COLOR = (255, 220, 0)
|
||||
|
||||
|
||||
def _draw_debug_marks(
|
||||
img: Image.Image, faces: tuple[FaceBox, ...], statuses: list[str]
|
||||
) -> None:
|
||||
"""Draw face boxes and a crosshair on the photo's own centre.
|
||||
|
||||
Drawn before cropping, so the marks get cut exactly like the photo: a
|
||||
crosshair that is off-centre or missing shows how much was cropped away.
|
||||
"""
|
||||
w, h = img.size
|
||||
draw = ImageDraw.Draw(img)
|
||||
line = max(2, round(min(w, h) / 200))
|
||||
for face, status in zip(faces, statuses):
|
||||
color = _DEBUG_COLORS[status]
|
||||
x1, y1, x2, y2 = face[:4]
|
||||
draw.rectangle((x1 * w, y1 * h, x2 * w, y2 * h), outline=color, width=line)
|
||||
px1, py1, px2, py2 = _padded_face(face)
|
||||
draw.rectangle(
|
||||
(px1 * w, py1 * h, px2 * w, py2 * h),
|
||||
outline=color,
|
||||
width=max(1, line // 2),
|
||||
)
|
||||
cx, cy = w / 2, h / 2
|
||||
arm = max(12, round(min(w, h) / 12))
|
||||
for width, color in ((line + 2, (0, 0, 0)), (line, _CROSSHAIR_COLOR)):
|
||||
draw.line((cx - arm, cy, cx + arm, cy), fill=color, width=width)
|
||||
draw.line((cx, cy - arm, cx, cy + arm), fill=color, width=width)
|
||||
radius = arm / 3
|
||||
draw.ellipse(
|
||||
(cx - radius, cy - radius, cx + radius, cy + radius),
|
||||
outline=_CROSSHAIR_COLOR,
|
||||
width=line,
|
||||
)
|
||||
|
||||
|
||||
def _draw_offscreen_centre(img: Image.Image, cx: float, cy: float) -> None:
|
||||
"""When the photo's centre was cropped away, point at it from the edge."""
|
||||
w, h = img.size
|
||||
if 0 <= cx <= w and 0 <= cy <= h:
|
||||
return
|
||||
size = max(10, round(min(w, h) / 20))
|
||||
x = max(size, min(w - size, cx))
|
||||
y = max(size, min(h - size, cy))
|
||||
if cx < 0:
|
||||
points = [(0, y), (size, y - size), (size, y + size)]
|
||||
elif cx > w:
|
||||
points = [(w, y), (w - size, y - size), (w - size, y + size)]
|
||||
elif cy < 0:
|
||||
points = [(x, 0), (x - size, size), (x + size, size)]
|
||||
else:
|
||||
points = [(x, h), (x - size, h - size), (x + size, h - size)]
|
||||
ImageDraw.Draw(img).polygon(points, fill=_CROSSHAIR_COLOR, outline=(0, 0, 0))
|
||||
|
||||
|
||||
def _debug_font(size: int):
|
||||
try:
|
||||
return ImageFont.load_default(size=size)
|
||||
except TypeError: # Pillow < 10.1 has a fixed-size default font
|
||||
return ImageFont.load_default()
|
||||
|
||||
|
||||
def _draw_debug_label(img: Image.Image, text: str) -> None:
|
||||
w, h = img.size
|
||||
draw = ImageDraw.Draw(img)
|
||||
size = max(12, round(min(w, h) / 30))
|
||||
font = _debug_font(size)
|
||||
pad = max(4, size // 3)
|
||||
x1, y1, x2, y2 = draw.textbbox((pad, pad), text, font=font)
|
||||
draw.rectangle((x1 - pad, y1 - pad, x2 + pad, y2 + pad), fill=(0, 0, 0))
|
||||
draw.text((pad, pad), text, fill=(255, 255, 255), font=font)
|
||||
|
||||
|
||||
def _resize_contain(
|
||||
img: Image.Image,
|
||||
target_w: int,
|
||||
target_h: int,
|
||||
bg=(0, 0, 0),
|
||||
hints: CropHints | None = None,
|
||||
) -> Image.Image:
|
||||
src_w, src_h = img.size
|
||||
if src_w <= 0 or src_h <= 0:
|
||||
return img.resize((target_w, target_h))
|
||||
@@ -276,6 +569,7 @@ def _resize_contain(img: Image.Image, target_w: int, target_h: int, bg=(0, 0, 0)
|
||||
new_w = max(1, int(src_w * scale))
|
||||
new_h = max(1, int(src_h * scale))
|
||||
resized = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
|
||||
_debug_uncropped(resized, hints)
|
||||
canvas = Image.new("RGB", (target_w, target_h), bg)
|
||||
rgb_resized = resized if resized.mode == "RGB" else resized.convert("RGB")
|
||||
canvas.paste(rgb_resized, ((target_w - new_w) // 2, (target_h - new_h) // 2))
|
||||
@@ -285,7 +579,22 @@ def _resize_contain(img: Image.Image, target_w: int, target_h: int, bg=(0, 0, 0)
|
||||
return canvas
|
||||
|
||||
|
||||
def _blur_fill(img: Image.Image, target_w: int, target_h: int) -> Image.Image:
|
||||
def _debug_uncropped(img: Image.Image, hints: CropHints | None) -> None:
|
||||
"""Debug marks for fill modes that never crop: every face is kept."""
|
||||
if hints is None or not hints.debug:
|
||||
return
|
||||
faces = hints.faces or ()
|
||||
statuses = [FACE_KEPT] * len(faces)
|
||||
_draw_debug_marks(img, faces, statuses)
|
||||
_draw_debug_label(img, _face_summary(hints.faces, statuses))
|
||||
|
||||
|
||||
def _blur_fill(
|
||||
img: Image.Image,
|
||||
target_w: int,
|
||||
target_h: int,
|
||||
hints: CropHints | None = None,
|
||||
) -> Image.Image:
|
||||
bg = _resize_cover(img, target_w, target_h).filter(ImageFilter.GaussianBlur(radius=24))
|
||||
src_w, src_h = img.size
|
||||
if src_w <= 0 or src_h <= 0:
|
||||
@@ -294,10 +603,10 @@ def _blur_fill(img: Image.Image, target_w: int, target_h: int) -> Image.Image:
|
||||
new_w = max(1, int(src_w * scale))
|
||||
new_h = max(1, int(src_h * scale))
|
||||
fg = img.resize((new_w, new_h), Image.Resampling.LANCZOS)
|
||||
_debug_uncropped(fg, hints)
|
||||
rgb_fg = fg if fg.mode == "RGB" else fg.convert("RGB")
|
||||
bg.paste(rgb_fg, ((target_w - new_w) // 2, (target_h - new_h) // 2))
|
||||
if rgb_fg is not fg:
|
||||
safe_close(rgb_fg)
|
||||
safe_close(fg)
|
||||
return bg
|
||||
|
||||
@@ -14,12 +14,15 @@ API shape (Immich v1.13x / v3, ``/api`` prefix, ``x-api-key`` header):
|
||||
``originalFileName`` but NOT ``exifInfo``.
|
||||
- ``GET /api/assets/{id}`` -> full asset incl ``exifInfo`` (lat/long, city,
|
||||
country, description) - used to enrich location/description per asset.
|
||||
- ``GET /api/faces?id={id}`` -> recognised faces and bounding boxes - used to
|
||||
keep faces whole in cover-mode crops.
|
||||
- Image bytes: ``/api/assets/{id}/thumbnail?size=preview|fullsize`` or
|
||||
``/api/assets/{id}/original`` (all require the ``x-api-key`` header).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
@@ -241,6 +244,198 @@ def parse_asset_exif(asset: Any) -> dict[str, Any]:
|
||||
return out
|
||||
|
||||
|
||||
def selected_person_ids(
|
||||
selection_type: str | None, selection_id: str | None
|
||||
) -> set[str]:
|
||||
"""Return the people explicitly selected for an Immich source.
|
||||
|
||||
Current entries use a composite JSON selection, while older entries can
|
||||
still carry the legacy ``person``/``people`` shapes. Album-only, favorite,
|
||||
random and custom-search sources intentionally return an empty set: there
|
||||
is no user-selected face to prioritise for those sources.
|
||||
"""
|
||||
if selection_type == "person":
|
||||
return {selection_id} if selection_id else set()
|
||||
if selection_type == "people":
|
||||
return {person_id for person_id in (selection_id or "").split(",") if person_id}
|
||||
if selection_type == "composite":
|
||||
return set(parse_composite_selection(selection_id)["people"])
|
||||
return set()
|
||||
|
||||
|
||||
def _normalised_face_box(
|
||||
face: Any,
|
||||
edits: list[dict[str, Any]] | None = None,
|
||||
original_size: tuple[float, float] | None = None,
|
||||
) -> tuple[float, float, float, float] | None:
|
||||
"""Return a face's bounding box normalised to 0..1, or None if invalid."""
|
||||
if not isinstance(face, dict):
|
||||
return None
|
||||
width = face.get("imageWidth")
|
||||
height = face.get("imageHeight")
|
||||
coords = (
|
||||
face.get("boundingBoxX1"),
|
||||
face.get("boundingBoxY1"),
|
||||
face.get("boundingBoxX2"),
|
||||
face.get("boundingBoxY2"),
|
||||
)
|
||||
if (
|
||||
any(isinstance(value, bool) or not isinstance(value, (int, float))
|
||||
or not math.isfinite(value) for value in (width, height, *coords))
|
||||
or width <= 0
|
||||
or height <= 0
|
||||
):
|
||||
return None
|
||||
x1, y1, x2, y2 = coords
|
||||
if x2 <= x1 or y2 <= y1:
|
||||
return None
|
||||
box = _original_face_box(
|
||||
(x1 / width, y1 / height, x2 / width, y2 / height),
|
||||
edits or [], original_size,
|
||||
)
|
||||
box = tuple(max(0.0, min(1.0, value)) for value in box)
|
||||
return box if box[2] > box[0] and box[3] > box[1] else None
|
||||
|
||||
|
||||
def parse_face_boxes(
|
||||
faces: Any,
|
||||
person_ids: set[str] | None = None,
|
||||
*,
|
||||
edits: list[dict[str, Any]] | None = None,
|
||||
original_size: tuple[float, float] | None = None,
|
||||
) -> list[list[float | bool]]:
|
||||
"""Return normalised ``[x1, y1, x2, y2, area, selected]`` face boxes.
|
||||
|
||||
Immich reports face boxes in the coordinate space described by each
|
||||
face's ``imageWidth``/``imageHeight``. Normalising each box makes it
|
||||
independent of whether Album Slideshow downloads a preview, full-size
|
||||
derivative or original. Faces Immich has not linked to a named person
|
||||
are included. Explicit person selection is a separate priority from area.
|
||||
"""
|
||||
if not isinstance(faces, list):
|
||||
return []
|
||||
boxes: list[list[float | bool]] = []
|
||||
for face in faces:
|
||||
box = _normalised_face_box(face, edits, original_size)
|
||||
if box is None:
|
||||
continue
|
||||
x1, y1, x2, y2 = box
|
||||
weight = (x2 - x1) * (y2 - y1)
|
||||
person = face.get("person")
|
||||
selected = bool(
|
||||
person_ids and isinstance(person, dict) and person.get("id") in person_ids
|
||||
)
|
||||
boxes.append([x1, y1, x2, y2, weight, selected])
|
||||
return boxes
|
||||
|
||||
|
||||
def _original_face_box(
|
||||
box: tuple[float, float, float, float],
|
||||
edits: list[dict[str, Any]],
|
||||
original_size: tuple[float, float] | None,
|
||||
) -> tuple[float, float, float, float]:
|
||||
points = [(box[0], box[1]), (box[2], box[3])]
|
||||
crop = None
|
||||
for edit in reversed(edits):
|
||||
if not isinstance(edit, dict) or not isinstance(edit.get("parameters"), dict):
|
||||
raise ValueError("Invalid Immich edit metadata")
|
||||
params = edit["parameters"]
|
||||
action = edit.get("action")
|
||||
if action == "rotate":
|
||||
angle = params.get("angle")
|
||||
if angle == 90:
|
||||
points = [(vertical, 1 - horizontal) for horizontal, vertical in points]
|
||||
elif angle == 180:
|
||||
points = [(1 - horizontal, 1 - vertical) for horizontal, vertical in points]
|
||||
elif angle == 270:
|
||||
points = [(1 - vertical, horizontal) for horizontal, vertical in points]
|
||||
elif angle != 0:
|
||||
raise ValueError("Unsupported Immich rotation")
|
||||
elif action == "mirror":
|
||||
axis = params.get("axis")
|
||||
if axis == "horizontal":
|
||||
points = [(horizontal, 1 - vertical) for horizontal, vertical in points]
|
||||
elif axis == "vertical":
|
||||
points = [(1 - horizontal, vertical) for horizontal, vertical in points]
|
||||
else:
|
||||
raise ValueError("Unsupported Immich mirror axis")
|
||||
elif action == "crop" and crop is None:
|
||||
crop = params
|
||||
else:
|
||||
raise ValueError("Unsupported Immich edit")
|
||||
if crop is not None:
|
||||
values = [crop.get(name) for name in ("x", "y", "width", "height")]
|
||||
if original_size is None or any(
|
||||
isinstance(value, bool) or not isinstance(value, (int, float))
|
||||
or not math.isfinite(value) for value in values
|
||||
):
|
||||
raise ValueError("Missing Immich crop dimensions")
|
||||
crop_left, crop_top, crop_width, crop_height = values
|
||||
original_width, original_height = original_size
|
||||
if (
|
||||
crop_left < 0 or crop_top < 0 or crop_width <= 0 or crop_height <= 0
|
||||
or crop_left + crop_width > original_width
|
||||
or crop_top + crop_height > original_height
|
||||
):
|
||||
raise ValueError("Invalid Immich crop dimensions")
|
||||
points = [
|
||||
((horizontal * crop_width + crop_left) / original_width,
|
||||
(vertical * crop_height + crop_top) / original_height)
|
||||
for horizontal, vertical in points
|
||||
]
|
||||
return (
|
||||
min(point[0] for point in points), min(point[1] for point in points),
|
||||
max(point[0] for point in points), max(point[1] for point in points),
|
||||
)
|
||||
|
||||
|
||||
def original_image_size(asset: Any) -> tuple[float, float] | None:
|
||||
info = asset.get("exifInfo") if isinstance(asset, dict) else None
|
||||
if not isinstance(info, dict):
|
||||
return None
|
||||
width, height = info.get("exifImageWidth"), info.get("exifImageHeight")
|
||||
if any(
|
||||
isinstance(value, bool) or not isinstance(value, (int, float))
|
||||
or not math.isfinite(value) or value <= 0 for value in (width, height)
|
||||
):
|
||||
return None
|
||||
if str(info.get("orientation")) in {"5", "6", "7", "8", "-90", "90"}:
|
||||
return (float(height), float(width))
|
||||
return (float(width), float(height))
|
||||
|
||||
|
||||
def parse_face_focus(
|
||||
faces: Any,
|
||||
person_ids: set[str],
|
||||
*,
|
||||
edits: list[dict[str, Any]] | None = None,
|
||||
original_size: tuple[float, float] | None = None,
|
||||
) -> tuple[float, float] | None:
|
||||
"""Return a normalised crop focus for the selected people in an asset.
|
||||
|
||||
Immich reports face boxes in the coordinate space described by each
|
||||
face's ``imageWidth``/``imageHeight``. Normalising each box makes the
|
||||
focus independent of resolution. Edited coordinates are mapped back to the
|
||||
unedited image before clamping. When several selected people appear in the
|
||||
same photo, focus on the centre of their combined region.
|
||||
"""
|
||||
if not isinstance(faces, list) or not person_ids:
|
||||
return None
|
||||
selected_faces = [
|
||||
face for face in faces
|
||||
if isinstance(face, dict) and isinstance(face.get("person"), dict)
|
||||
and face["person"].get("id") in person_ids
|
||||
]
|
||||
boxes = parse_face_boxes(selected_faces, edits=edits, original_size=original_size)
|
||||
if not boxes:
|
||||
return None
|
||||
left = min(box[0] for box in boxes)
|
||||
top = min(box[1] for box in boxes)
|
||||
right = max(box[2] for box in boxes)
|
||||
bottom = max(box[3] for box in boxes)
|
||||
return ((left + right) / 2, (top + bottom) / 2)
|
||||
|
||||
|
||||
class ImmichClient:
|
||||
"""Thin async wrapper over the Immich REST API."""
|
||||
|
||||
@@ -373,3 +568,15 @@ class ImmichClient:
|
||||
|
||||
async def async_get_asset(self, asset_id: str) -> dict[str, Any]:
|
||||
return await self._get(f"/api/assets/{asset_id}")
|
||||
|
||||
async def async_get_faces(self, asset_id: str) -> list[dict[str, Any]]:
|
||||
data = await self._get(f"/api/faces?id={asset_id}")
|
||||
if not isinstance(data, list):
|
||||
raise ValueError("Invalid Immich face response")
|
||||
return data
|
||||
|
||||
async def async_get_asset_edits(self, asset_id: str) -> list[dict[str, Any]]:
|
||||
data = await self._get(f"/api/assets/{asset_id}/edits")
|
||||
if not isinstance(data, dict) or not isinstance(data.get("edits"), list):
|
||||
raise ValueError("Invalid Immich edit response")
|
||||
return data["edits"]
|
||||
@@ -7,6 +7,6 @@
|
||||
"documentation": "https://github.com/eyalgal/album_slideshow",
|
||||
"iot_class": "cloud_polling",
|
||||
"issue_tracker": "https://github.com/eyalgal/album_slideshow/issues",
|
||||
"requirements": ["Pillow", "PyNaCl>=1.5.0"],
|
||||
"version": "1.11.0"
|
||||
"requirements": ["PyNaCl>=1.5.0"],
|
||||
"version": "1.13.0"
|
||||
}
|
||||
@@ -6,7 +6,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import DOMAIN, MAX_CUSTOM_LOOKBACK_DAYS
|
||||
from .coordinator import AlbumCoordinator
|
||||
from .store import SlideshowStore
|
||||
|
||||
@@ -23,6 +23,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e
|
||||
PairMinGapPercentNumber(entry, store),
|
||||
NavigationBufferSizeNumber(entry, store),
|
||||
ImageCacheMbNumber(entry, store),
|
||||
CustomLookbackDaysNumber(entry, store),
|
||||
ShuffleAgeBiasNumber(entry, store),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -49,6 +51,66 @@ class _BaseNumber(NumberEntity, RestoreEntity):
|
||||
}
|
||||
|
||||
|
||||
class CustomLookbackDaysNumber(_BaseNumber):
|
||||
_attr_icon = "mdi:calendar-range"
|
||||
_attr_native_min_value = 1
|
||||
_attr_native_max_value = MAX_CUSTOM_LOOKBACK_DAYS
|
||||
_attr_native_step = 1
|
||||
_attr_native_unit_of_measurement = "d"
|
||||
|
||||
def __init__(self, entry: ConfigEntry, store: SlideshowStore) -> None:
|
||||
super().__init__(entry, store)
|
||||
self._attr_unique_id = f"{entry.entry_id}_custom_lookback_days"
|
||||
self._attr_name = "Custom lookback days"
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
return int(self.store.custom_lookback_days)
|
||||
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
self.store.custom_lookback_days = max(1, min(MAX_CUSTOM_LOOKBACK_DAYS, int(value)))
|
||||
self.store.notify()
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
await super().async_added_to_hass()
|
||||
old = await self.async_get_last_state()
|
||||
if old and old.state not in (None, "unknown", "unavailable"):
|
||||
try:
|
||||
await self.async_set_native_value(float(old.state))
|
||||
except (TypeError, ValueError, OverflowError):
|
||||
return
|
||||
|
||||
|
||||
class ShuffleAgeBiasNumber(_BaseNumber):
|
||||
_attr_icon = "mdi:shuffle-variant"
|
||||
_attr_native_min_value = -100
|
||||
_attr_native_max_value = 100
|
||||
_attr_native_step = 1
|
||||
_attr_mode = NumberMode.SLIDER
|
||||
|
||||
def __init__(self, entry: ConfigEntry, store: SlideshowStore) -> None:
|
||||
super().__init__(entry, store)
|
||||
self._attr_unique_id = f"{entry.entry_id}_shuffle_age_bias"
|
||||
self._attr_name = "Shuffle age bias"
|
||||
|
||||
@property
|
||||
def native_value(self):
|
||||
return int(self.store.shuffle_age_bias)
|
||||
|
||||
async def async_set_native_value(self, value: float) -> None:
|
||||
self.store.shuffle_age_bias = max(-100, min(100, int(value)))
|
||||
self.store.notify()
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
await super().async_added_to_hass()
|
||||
old = await self.async_get_last_state()
|
||||
if old and old.state not in (None, "unknown", "unavailable"):
|
||||
try:
|
||||
await self.async_set_native_value(float(old.state))
|
||||
except (TypeError, ValueError, OverflowError):
|
||||
return
|
||||
|
||||
|
||||
class SlideIntervalNumber(_BaseNumber):
|
||||
_attr_icon = "mdi:timer-outline"
|
||||
_attr_native_min_value = 3
|
||||
|
||||
@@ -7,9 +7,11 @@ implementation. Operates on ``MediaItem``-like objects that expose
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
import math
|
||||
from typing import Iterable, TypeVar
|
||||
|
||||
from .const import (
|
||||
DATE_FILTER_CUSTOM,
|
||||
DATE_FILTER_LAST_7,
|
||||
DATE_FILTER_LAST_30,
|
||||
DATE_FILTER_LAST_365,
|
||||
@@ -17,7 +19,9 @@ from .const import (
|
||||
DATE_FILTER_ON_THIS_DAY,
|
||||
DATE_FILTER_THIS_MONTH,
|
||||
DATE_FILTER_THIS_YEAR,
|
||||
DEFAULT_CUSTOM_LOOKBACK_DAYS,
|
||||
DEFAULT_MISSING_DATE_MODE,
|
||||
MAX_CUSTOM_LOOKBACK_DAYS,
|
||||
MISSING_DATE_EXCLUDE,
|
||||
MISSING_DATE_USE_UPLOADED,
|
||||
ORDER_ALBUM,
|
||||
@@ -31,6 +35,39 @@ from .const import (
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def age_weights(
|
||||
items: list[T],
|
||||
bias: float,
|
||||
*,
|
||||
missing_date: str = DEFAULT_MISSING_DATE_MODE,
|
||||
) -> list[float]:
|
||||
"""Return relative draw weights, from 1 to 10, favoring newer (+) or older (-)."""
|
||||
if isinstance(bias, bool) or not isinstance(bias, (int, float)) or not math.isfinite(bias):
|
||||
bias = 0
|
||||
bias = max(-100, min(100, bias))
|
||||
if not bias:
|
||||
return [1.0] * len(items)
|
||||
timestamps = []
|
||||
for item in items:
|
||||
timestamp = getattr(item, "captured_at", None)
|
||||
if type(timestamp) is not int and missing_date == MISSING_DATE_USE_UPLOADED:
|
||||
timestamp = getattr(item, "uploaded_at", None)
|
||||
timestamps.append(timestamp if type(timestamp) is int else None)
|
||||
dated = [timestamp for timestamp in timestamps if timestamp is not None]
|
||||
if not dated or min(dated) == max(dated):
|
||||
return [1.0] * len(items)
|
||||
oldest, newest = min(dated), max(dated)
|
||||
strength = 9 * abs(bias) / 100
|
||||
weights = []
|
||||
for timestamp in timestamps:
|
||||
if timestamp is None:
|
||||
weights.append(1.0)
|
||||
continue
|
||||
position = (timestamp - oldest) / (newest - oldest)
|
||||
weights.append(1 + strength * (position if bias > 0 else 1 - position))
|
||||
return weights
|
||||
|
||||
|
||||
def order_items(items: list[T], order_mode: str) -> list[T]:
|
||||
"""Return a new list ordered per ``order_mode``.
|
||||
|
||||
@@ -76,6 +113,7 @@ def filter_items(
|
||||
*,
|
||||
mode: str,
|
||||
missing_date: str = DEFAULT_MISSING_DATE_MODE,
|
||||
lookback_days: int = DEFAULT_CUSTOM_LOOKBACK_DAYS,
|
||||
now: datetime | None = None,
|
||||
) -> list[T]:
|
||||
"""Filter items by date according to ``mode``.
|
||||
@@ -97,7 +135,7 @@ def filter_items(
|
||||
|
||||
today_utc = (now or datetime.now(timezone.utc)).astimezone(timezone.utc)
|
||||
|
||||
pred, strict = _build_predicate(mode, today_utc)
|
||||
pred, strict = _build_predicate(mode, today_utc, lookback_days)
|
||||
if pred is None:
|
||||
return list(items)
|
||||
|
||||
@@ -130,8 +168,14 @@ def filter_items(
|
||||
def _build_predicate(
|
||||
mode: str,
|
||||
today_utc: datetime,
|
||||
lookback_days: int = DEFAULT_CUSTOM_LOOKBACK_DAYS,
|
||||
):
|
||||
"""Return (predicate, strict). ``strict`` drops items without timestamps."""
|
||||
if mode == DATE_FILTER_CUSTOM:
|
||||
days = lookback_days if type(lookback_days) is int else DEFAULT_CUSTOM_LOOKBACK_DAYS
|
||||
days = max(1, min(MAX_CUSTOM_LOOKBACK_DAYS, days))
|
||||
cutoff = int((today_utc - timedelta(days=days)).timestamp() * 1000)
|
||||
return (lambda timestamp: timestamp >= cutoff), False
|
||||
if mode == DATE_FILTER_LAST_7:
|
||||
cutoff = int((today_utc - timedelta(days=7)).timestamp() * 1000)
|
||||
return (lambda ts: ts >= cutoff), False
|
||||
|
||||
@@ -22,7 +22,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_e
|
||||
CacheUsageSensor(entry, coordinator),
|
||||
HiddenPhotoCountSensor(entry, coordinator),
|
||||
]
|
||||
if coordinator.provider in ENRICHING_PROVIDERS:
|
||||
if (
|
||||
coordinator.provider in ENRICHING_PROVIDERS
|
||||
or coordinator.google_metadata_enabled
|
||||
or coordinator.google_location_enabled
|
||||
):
|
||||
# Diagnostic surface for the background enrichment pass (per-photo
|
||||
# metadata reads and/or reverse-geocode). Providers with nothing to
|
||||
# enrich omit it to keep the device screen tidy.
|
||||
|
||||
@@ -13,6 +13,7 @@ from .const import (
|
||||
DEFAULT_FILL_MODE,
|
||||
DEFAULT_ORIENTATION_MISMATCH_MODE,
|
||||
DEFAULT_ORDER_MODE,
|
||||
DEFAULT_SHUFFLE_AGE_BIAS,
|
||||
DEFAULT_ASPECT_RATIO,
|
||||
DEFAULT_PAIR_DIVIDER_PX,
|
||||
DEFAULT_PAIR_DIVIDER_COLOR,
|
||||
@@ -21,6 +22,7 @@ from .const import (
|
||||
DEFAULT_NAVIGATION_BUFFER_SIZE,
|
||||
DEFAULT_MAX_RESOLUTION,
|
||||
DEFAULT_DATE_FILTER,
|
||||
DEFAULT_CUSTOM_LOOKBACK_DAYS,
|
||||
DEFAULT_MISSING_DATE_MODE,
|
||||
)
|
||||
|
||||
@@ -35,6 +37,7 @@ class SlideshowStore:
|
||||
fill_mode: str = DEFAULT_FILL_MODE
|
||||
portrait_mode: str = DEFAULT_ORIENTATION_MISMATCH_MODE
|
||||
order_mode: str = DEFAULT_ORDER_MODE
|
||||
shuffle_age_bias: int = DEFAULT_SHUFFLE_AGE_BIAS
|
||||
aspect_ratio: str = DEFAULT_ASPECT_RATIO
|
||||
pair_divider_px: int = DEFAULT_PAIR_DIVIDER_PX
|
||||
pair_divider_color: str = DEFAULT_PAIR_DIVIDER_COLOR
|
||||
@@ -45,6 +48,7 @@ class SlideshowStore:
|
||||
|
||||
# Date filter mode (preset windows like this_year / on_this_day).
|
||||
date_filter: str = DEFAULT_DATE_FILTER
|
||||
custom_lookback_days: int = DEFAULT_CUSTOM_LOOKBACK_DAYS
|
||||
|
||||
# How the date filter treats photos with no EXIF capture date.
|
||||
missing_date_mode: str = DEFAULT_MISSING_DATE_MODE
|
||||
@@ -52,6 +56,9 @@ class SlideshowStore:
|
||||
# Pause toggle - when True, the slideshow holds on the current frame.
|
||||
paused: bool = False
|
||||
|
||||
# Draw face boxes, a centre crosshair and a crop summary on each slide.
|
||||
face_debug: bool = False
|
||||
|
||||
# In-memory last rendered frame. Not user-configurable; used to re-serve
|
||||
# the previous slide instantly across a camera reload.
|
||||
last_frame: bytes | None = None
|
||||
|
||||
@@ -172,19 +172,55 @@
|
||||
"nextcloud_cannot_connect": "Could not connect to Nextcloud. Check the URL, username, app password and folder path.",
|
||||
"invalid_nextcloud_url": "That does not look like a Nextcloud Photos public album link.",
|
||||
"nextcloud_public_cannot_connect": "Could not connect to that Nextcloud album. Check the link is still valid.",
|
||||
"invalid_ente_url": "That does not look like an Ente public album link. It should look like {example_url}, including the part after the #."
|
||||
,"ente_cannot_connect": "Could not open that Ente album. The link may be expired, revoked, or password-protected."
|
||||
"invalid_ente_url": "That does not look like an Ente public album link. It should look like {example_url}, including the part after the #.",
|
||||
"ente_cannot_connect": "Could not open that Ente album. The link may be expired, revoked, or password-protected."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"google_metadata": {
|
||||
"title": "Google Photos metadata and privacy",
|
||||
"description": "Photo descriptions, camera details, and exposure metadata use Google's undocumented public endpoint without a Google login. Results are cached; unavailable metadata does not prevent the slideshow from loading.\n\nOriginal photo GPS is a separate opt-in, off by default. It reads up to 256 KB per photo. Google only includes GPS in shared originals when location sharing is turned on for the album; anyone with the album link can then see precise GPS. After changing the album's location sharing, turn this off and back on to rescan. Coordinates are cached in Home Assistant and exposed in camera attributes. Google-added or estimated locations are not included.\n\nPlace-name lookup is also off by default. It requires original GPS reading and sends coordinates to the public OpenStreetMap Nominatim service. Disabling original GPS hides coordinates and place names and stops both lookups; it does not erase previous history or backups.",
|
||||
"data": {
|
||||
"google_metadata": "Fetch photo metadata (experimental)",
|
||||
"google_location": "Read original photo GPS (opt-in)",
|
||||
"reverse_geocode": "Look up GPS place names via OpenStreetMap (opt-in)"
|
||||
}
|
||||
},
|
||||
"init": {
|
||||
"title": "Location & privacy options",
|
||||
"description": "Reverse geocoding sends your photos' EXIF GPS coordinates to the public OpenStreetMap Nominatim service to look up a human-readable place name. Coordinates are rounded to ~100 m before lookup and cached on disk. Turn this off to keep coordinates entirely local; the latitude and longitude attributes still work either way.",
|
||||
"data": {
|
||||
"reverse_geocode": "Reverse-geocode EXIF GPS coordinates via OpenStreetMap"
|
||||
}
|
||||
},
|
||||
"immich_select": {
|
||||
"title": "Immich source",
|
||||
"description": "Change which albums, people and favorites this slideshow shows. Saving reloads the slideshow; its entities, settings and hidden photos are kept.",
|
||||
"data": {
|
||||
"album_name": "Name",
|
||||
"albums": "Albums",
|
||||
"people": "People",
|
||||
"favorites": "Include favorites",
|
||||
"immich_filter": "Extra search filter (JSON, optional)",
|
||||
"immich_image_size": "Image quality",
|
||||
"reverse_geocode": "Look up EXIF GPS locations via OpenStreetMap"
|
||||
}
|
||||
},
|
||||
"connection": {
|
||||
"title": "Immich connection",
|
||||
"description": "The stored Immich URL or API key no longer works. Update them to continue.",
|
||||
"data": {
|
||||
"immich_url": "Immich URL (e.g. http://192.168.1.10:2283)",
|
||||
"immich_api_key": "API key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"immich_cache_clear_failed": "Could not clear the previous photo source cache. No source settings were changed. Please try again.",
|
||||
"immich_cannot_connect": "Could not connect to Immich. Check the URL and API key.",
|
||||
"immich_invalid_auth": "Immich rejected the API key. Check that it is valid and that its permissions include server.about, album.read and person.read.",
|
||||
"immich_filter_invalid": "The filter must be a valid JSON object. See the README for examples."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
|
||||
@@ -12,7 +13,7 @@ from .store import SlideshowStore
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback) -> None:
|
||||
store: SlideshowStore = hass.data[DOMAIN][entry.entry_id]["store"]
|
||||
async_add_entities([PauseSwitch(entry, store)])
|
||||
async_add_entities([PauseSwitch(entry, store), CropDebugSwitch(entry, store)])
|
||||
|
||||
|
||||
class PauseSwitch(SwitchEntity, RestoreEntity):
|
||||
@@ -53,3 +54,46 @@ class PauseSwitch(SwitchEntity, RestoreEntity):
|
||||
if old is not None and old.state in ("on", "off"):
|
||||
self.store.paused = old.state == "on"
|
||||
self.store.notify()
|
||||
|
||||
|
||||
class CropDebugSwitch(SwitchEntity, RestoreEntity):
|
||||
"""Overlay face boxes, a centre crosshair and a crop summary on slides."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_should_poll = False
|
||||
_attr_icon = "mdi:face-recognition"
|
||||
_attr_entity_category = EntityCategory.CONFIG
|
||||
|
||||
def __init__(self, entry: ConfigEntry, store: SlideshowStore) -> None:
|
||||
self.entry = entry
|
||||
self.store = store
|
||||
self._attr_unique_id = f"{entry.entry_id}_crop_debug"
|
||||
self._attr_name = "Crop debug overlay"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
return bool(self.store.face_debug)
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.entry.entry_id)},
|
||||
"name": f"Album Slideshow {self.entry.title}",
|
||||
"manufacturer": "Album Slideshow",
|
||||
}
|
||||
|
||||
async def async_turn_on(self, **kwargs) -> None:
|
||||
self.store.face_debug = True
|
||||
self.store.notify()
|
||||
|
||||
async def async_turn_off(self, **kwargs) -> None:
|
||||
self.store.face_debug = False
|
||||
self.store.notify()
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
await super().async_added_to_hass()
|
||||
self.store.add_listener(self.async_write_ha_state)
|
||||
old = await self.async_get_last_state()
|
||||
if old is not None and old.state in ("on", "off"):
|
||||
self.store.face_debug = old.state == "on"
|
||||
self.store.notify()
|
||||
@@ -172,19 +172,55 @@
|
||||
"nextcloud_cannot_connect": "Could not connect to Nextcloud. Check the URL, username, app password and folder path.",
|
||||
"invalid_nextcloud_url": "That does not look like a Nextcloud Photos public album link.",
|
||||
"nextcloud_public_cannot_connect": "Could not connect to that Nextcloud album. Check the link is still valid.",
|
||||
"invalid_ente_url": "That does not look like an Ente public album link. It should look like {example_url}, including the part after the #."
|
||||
,"ente_cannot_connect": "Could not open that Ente album. The link may be expired, revoked, or password-protected."
|
||||
"invalid_ente_url": "That does not look like an Ente public album link. It should look like {example_url}, including the part after the #.",
|
||||
"ente_cannot_connect": "Could not open that Ente album. The link may be expired, revoked, or password-protected."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"google_metadata": {
|
||||
"title": "Google Photos metadata and privacy",
|
||||
"description": "Photo descriptions, camera details, and exposure metadata use Google's undocumented public endpoint without a Google login. Results are cached; unavailable metadata does not prevent the slideshow from loading.\n\nOriginal photo GPS is a separate opt-in, off by default. It reads up to 256 KB per photo. Google only includes GPS in shared originals when location sharing is turned on for the album; anyone with the album link can then see precise GPS. After changing the album's location sharing, turn this off and back on to rescan. Coordinates are cached in Home Assistant and exposed in camera attributes. Google-added or estimated locations are not included.\n\nPlace-name lookup is also off by default. It requires original GPS reading and sends coordinates to the public OpenStreetMap Nominatim service. Disabling original GPS hides coordinates and place names and stops both lookups; it does not erase previous history or backups.",
|
||||
"data": {
|
||||
"google_metadata": "Fetch photo metadata (experimental)",
|
||||
"google_location": "Read original photo GPS (opt-in)",
|
||||
"reverse_geocode": "Look up GPS place names via OpenStreetMap (opt-in)"
|
||||
}
|
||||
},
|
||||
"init": {
|
||||
"title": "Location & privacy options",
|
||||
"description": "Reverse geocoding sends your photos' EXIF GPS coordinates to the public OpenStreetMap Nominatim service to look up a human-readable place name. Coordinates are rounded to ~100 m before lookup and cached on disk. Turn this off to keep coordinates entirely local; the latitude and longitude attributes still work either way.",
|
||||
"data": {
|
||||
"reverse_geocode": "Reverse-geocode EXIF GPS coordinates via OpenStreetMap"
|
||||
}
|
||||
},
|
||||
"immich_select": {
|
||||
"title": "Immich source",
|
||||
"description": "Change which albums, people and favorites this slideshow shows. Saving reloads the slideshow; its entities, settings and hidden photos are kept.",
|
||||
"data": {
|
||||
"album_name": "Name",
|
||||
"albums": "Albums",
|
||||
"people": "People",
|
||||
"favorites": "Include favorites",
|
||||
"immich_filter": "Extra search filter (JSON, optional)",
|
||||
"immich_image_size": "Image quality",
|
||||
"reverse_geocode": "Look up EXIF GPS locations via OpenStreetMap"
|
||||
}
|
||||
},
|
||||
"connection": {
|
||||
"title": "Immich connection",
|
||||
"description": "The stored Immich URL or API key no longer works. Update them to continue.",
|
||||
"data": {
|
||||
"immich_url": "Immich URL (e.g. http://192.168.1.10:2283)",
|
||||
"immich_api_key": "API key"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"immich_cache_clear_failed": "Could not clear the previous photo source cache. No source settings were changed. Please try again.",
|
||||
"immich_cannot_connect": "Could not connect to Immich. Check the URL and API key.",
|
||||
"immich_invalid_auth": "Immich rejected the API key. Check that it is valid and that its permissions include server.about, album.read and person.read.",
|
||||
"immich_filter_invalid": "The filter must be a valid JSON object. See the README for examples."
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -14,6 +14,7 @@ from homeassistant.auth import EVENT_USER_REMOVED
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_ENTITY_ID, EVENT_HOMEASSISTANT_STOP
|
||||
from homeassistant.core import (
|
||||
CoreState,
|
||||
Event,
|
||||
HomeAssistant,
|
||||
ServiceCall,
|
||||
@@ -1502,6 +1503,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: MaintenanceSupporterConf
|
||||
await todo_mirror.async_setup()
|
||||
hass.data[DOMAIN][TODO_MIRROR_KEY] = todo_mirror
|
||||
|
||||
# 2026-10 places: tell a person what is due where they just arrived —
|
||||
# same lifecycle as the mirrors (global entry owns it).
|
||||
from .helpers.places import PLACES_KEY, PlaceArrivals
|
||||
|
||||
if PLACES_KEY not in hass.data[DOMAIN]:
|
||||
place_arrivals = PlaceArrivals(hass)
|
||||
place_arrivals.async_setup()
|
||||
hass.data[DOMAIN][PLACES_KEY] = place_arrivals
|
||||
|
||||
# One-time migration: auto-enable advanced feature flags for existing users
|
||||
options = dict(entry.options or entry.data)
|
||||
if CONF_ADVANCED_ADAPTIVE not in options:
|
||||
@@ -1727,6 +1737,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: MaintenanceSupporterConf
|
||||
entry.runtime_data = MaintenanceSupporterData(coordinator=coordinator, store=store)
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
# A shopping-list row checked while this object was not loaded (a
|
||||
# reload after a task edit, a setup retry) waits for it — the resync
|
||||
# books it now. At startup the pass after "started" covers it.
|
||||
if hass.state is CoreState.running:
|
||||
from .shopping_sync import schedule_resync
|
||||
|
||||
schedule_resync(hass)
|
||||
|
||||
# #148: a battery type that becomes known only after fleet setup (a
|
||||
# Battery Notes note added later, a typed low-only Matter binary)
|
||||
# never got its spare part — reconcile once per start, after the
|
||||
@@ -2118,6 +2136,11 @@ async def async_unload_entry(hass: HomeAssistant, entry: MaintenanceSupporterCon
|
||||
todo_mirror = hass.data.get(DOMAIN, {}).pop(TODO_MIRROR_KEY, None)
|
||||
if todo_mirror is not None:
|
||||
todo_mirror.async_teardown()
|
||||
from .helpers.places import PLACES_KEY
|
||||
|
||||
place_arrivals = hass.data.get(DOMAIN, {}).pop(PLACES_KEY, None)
|
||||
if place_arrivals is not None:
|
||||
place_arrivals.async_teardown()
|
||||
# NOT the full-text index: it is shared runtime (built once per boot
|
||||
# by _async_setup_shared, which a hub reload does not re-run) — a
|
||||
# reload of the global entry (any settings save) closed it for the
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import logging
|
||||
from datetime import date, datetime, time, timedelta
|
||||
from typing import TYPE_CHECKING, Any
|
||||
@@ -700,6 +701,12 @@ class MaintenanceCalendar(CalendarEntity):
|
||||
else:
|
||||
live_tasks = {}
|
||||
|
||||
# 2026-10 places: an object maintained away from home names its
|
||||
# place as the events' location (phone calendars show it).
|
||||
from .helpers.places import object_place, zone_name
|
||||
|
||||
place_name = zone_name(self._hass, object_place(entry.data.get(CONF_OBJECT)))
|
||||
|
||||
# Merge static (ConfigEntry) + dynamic (Store) task data
|
||||
tasks_data = merged_tasks(entry)
|
||||
schedule_time_enabled = self._is_schedule_time_feature_enabled()
|
||||
@@ -731,6 +738,8 @@ class MaintenanceCalendar(CalendarEntity):
|
||||
|
||||
event = self._create_event_for_task(task, obj_name, start_d, end_d, live_status=live.get("_status"))
|
||||
if event:
|
||||
if place_name:
|
||||
event = dataclasses.replace(event, location=place_name)
|
||||
events.append(event)
|
||||
|
||||
return events
|
||||
|
||||
@@ -29,6 +29,7 @@ from .const import (
|
||||
CONF_ADVANCED_COMPLETION_ACTIONS,
|
||||
CONF_ADVANCED_ENVIRONMENTAL,
|
||||
CONF_ADVANCED_GROUPS,
|
||||
CONF_ADVANCED_PLACES,
|
||||
CONF_ADVANCED_SCHEDULE_TIME,
|
||||
CONF_ADVANCED_SEASONAL,
|
||||
CONF_ARCHIVE_ONEOFF_DAYS,
|
||||
@@ -771,6 +772,10 @@ class GlobalOptionsFlow(OptionsFlow):
|
||||
CONF_ADVANCED_COMPLETION_ACTIONS,
|
||||
default=self._opt(CONF_ADVANCED_COMPLETION_ACTIONS),
|
||||
): selector.BooleanSelector(),
|
||||
vol.Optional(
|
||||
CONF_ADVANCED_PLACES,
|
||||
default=self._opt(CONF_ADVANCED_PLACES),
|
||||
): selector.BooleanSelector(),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
@@ -197,17 +197,14 @@ class TriggerStepsMixin(TriggerConfigMixin):
|
||||
def _clear_stale_trigger_runtime(self, old_tc: dict[str, Any], new_tc: dict[str, Any]) -> None:
|
||||
"""Drop persisted trigger runtime when the trigger fundamentally changed.
|
||||
|
||||
Mirrors the WS update path (websocket/tasks_crud.py): the Store
|
||||
runtime wins over config on restore (#102), so an options-flow edit
|
||||
that changes type/entities/baseline must clear it or the old
|
||||
The same rule as the WS update path (trigger_runtime_stale): the
|
||||
Store runtime wins over config on restore (#102), so an options-flow
|
||||
edit that changes type/entities/baseline must clear it or the old
|
||||
counters/anchors silently survive the edit (bug audit 2026-08-22).
|
||||
"""
|
||||
if (
|
||||
old_tc.get("type") != new_tc.get("type")
|
||||
or old_tc.get("entity_id") != new_tc.get("entity_id")
|
||||
or old_tc.get("entity_ids") != new_tc.get("entity_ids")
|
||||
or old_tc.get("trigger_baseline_value") != new_tc.get("trigger_baseline_value")
|
||||
):
|
||||
from .entity.triggers import trigger_runtime_stale
|
||||
|
||||
if trigger_runtime_stale(old_tc, new_tc):
|
||||
rd = getattr(self.config_entry, "runtime_data", None)
|
||||
store = getattr(rd, "store", None) if rd else None
|
||||
if store is not None:
|
||||
|
||||
@@ -162,6 +162,9 @@ CONF_ADVANCED_SCHEDULE_TIME = "advanced_schedule_time_visible"
|
||||
# Print-QR generator. Default OFF — beginners aren't overwhelmed; data
|
||||
# still persists if a user toggles the flag off again.
|
||||
CONF_ADVANCED_COMPLETION_ACTIONS = "advanced_completion_actions_visible"
|
||||
# 2026-10: shows the object's place (an HA zone) and "remind only on site" in
|
||||
# the panel. Visibility only, like the others: a place set before keeps working.
|
||||
CONF_ADVANCED_PLACES = "advanced_places_visible"
|
||||
|
||||
# Panel-access overrides: HA user IDs who get the full admin panel despite
|
||||
# not being HA admins. Empty list means only admins see the full panel.
|
||||
@@ -419,6 +422,10 @@ VENDOR_URL = "/maintenance_supporter_vendor"
|
||||
CONF_OBJECT = "object"
|
||||
CONF_OBJECT_NAME = "name"
|
||||
CONF_OBJECT_AREA = "area_id"
|
||||
# 2026-10 places: an HA zone the object is maintained at (None = home), and
|
||||
# whether its reminders wait until somebody is there (helpers/places.py).
|
||||
CONF_OBJECT_PLACE = "place"
|
||||
CONF_OBJECT_REMIND_ON_SITE = "remind_on_site"
|
||||
CONF_OBJECT_MANUFACTURER = "manufacturer"
|
||||
CONF_OBJECT_MODEL = "model"
|
||||
CONF_OBJECT_SERIAL_NUMBER = "serial_number"
|
||||
@@ -838,6 +845,12 @@ SIGNAL_DOCUMENTS_UPDATED = f"{DOMAIN}_documents_updated"
|
||||
|
||||
# --- Trigger Completion Cooldown ---
|
||||
TRIGGER_COMPLETION_COOLDOWN_SECONDS = 600 # 10 minutes
|
||||
|
||||
# Places (helpers/places.py): a person must stay this long in a zone before
|
||||
# the arrival message goes out (a drive past a zone edge is no visit), and
|
||||
# gets it at most once per this window for the same place.
|
||||
PLACE_ARRIVAL_DWELL_SECONDS = 120
|
||||
PLACE_ARRIVAL_COOLDOWN_SECONDS = 6 * 3600
|
||||
# Household double-complete window: two people tapping Complete on the same
|
||||
# task within this many seconds count as ONE real-world action (journey M1).
|
||||
# Short on purpose — a deliberate complete → reset → complete-again correction
|
||||
|
||||
@@ -89,6 +89,10 @@ _TRIGGER_EPOCH_TYPES = frozenset(
|
||||
}
|
||||
)
|
||||
|
||||
# Trigger types whose refresh-time fallback reads the live entity states (the
|
||||
# others read persisted counters): only a reading may end their episode.
|
||||
_STATE_READING_TRIGGER_TYPES = frozenset({"threshold", "counter", "due_date"})
|
||||
|
||||
|
||||
def _takes_day_suggestion(schedule: Schedule) -> bool:
|
||||
"""True when a suggested interval IN DAYS can be applied to the schedule:
|
||||
@@ -706,6 +710,14 @@ class MaintenanceCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
task._trigger_current_value = result.current_value
|
||||
if in_cooldown:
|
||||
return # the value for display only — no re-activation yet
|
||||
if result.active is False and trigger_type in _STATE_READING_TRIGGER_TYPES and not self._trigger_has_reading(entity_ids):
|
||||
# An episode ends on a reading, never on sensors that are all
|
||||
# offline. The evaluators already give no verdict then; this
|
||||
# keeps the rule for any evaluator to come: a counter read as
|
||||
# "not reached" while it was unavailable flipped the task to OK
|
||||
# and back, and the next restart announced the same activation
|
||||
# again (live installation 2026-10-04).
|
||||
return
|
||||
if result.active is not None:
|
||||
if task._trigger_active and not result.active:
|
||||
# A recovery only this sweep saw still ends the episode.
|
||||
@@ -1959,6 +1971,15 @@ class MaintenanceCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||
task_result["_trigger_current_value"] = value
|
||||
task_result["_status"] = compute_status_from_task_dict(task_result)
|
||||
|
||||
def _trigger_has_reading(self, entity_ids: list[str]) -> bool:
|
||||
"""Whether at least one of a trigger's entities has a state at all
|
||||
(exists, and is not unavailable / unknown)."""
|
||||
for entity_id in entity_ids:
|
||||
state = self.hass.states.get(entity_id)
|
||||
if state is not None and state.state not in UNAVAILABLE_STATES:
|
||||
return True
|
||||
return False
|
||||
|
||||
def note_trigger_cleared(self, task_id: str) -> None:
|
||||
"""The task's trigger episode ended (every entity back in range).
|
||||
Persisted, so :meth:`trigger_already_announced` sees the episode
|
||||
|
||||
@@ -70,6 +70,28 @@ def primary_entity_id(trigger_config: Any) -> str | None:
|
||||
return next((eid for eid in normalize_entity_ids(trigger_config) if isinstance(eid, str) and eid), None)
|
||||
|
||||
|
||||
def trigger_runtime_stale(old: Any, new: Any) -> bool:
|
||||
"""Whether a trigger edit invalidates the task's persisted trigger
|
||||
runtime (counter baselines, runtime hours, change counts): another type,
|
||||
other entities or an edited counting start value. The ONE rule for the
|
||||
WebSocket update and the options flow.
|
||||
|
||||
The entities are compared normalized. An old task stores only the legacy
|
||||
``entity_id``, while every validated edit carries ``entity_ids`` too;
|
||||
comparing the raw keys read that as "other entities", so the first save
|
||||
of such a task wiped its baseline or hours even when only the target
|
||||
changed (live installation 2026-10-04). A compound keeps its runtime per
|
||||
condition and, as before, only a type change resets it.
|
||||
"""
|
||||
old_tc = old if isinstance(old, dict) else {}
|
||||
new_tc = new if isinstance(new, dict) else {}
|
||||
if old_tc.get("type") != new_tc.get("type"):
|
||||
return True
|
||||
if new_tc.get("type") != TriggerType.COMPOUND and normalize_entity_ids(old_tc) != normalize_entity_ids(new_tc):
|
||||
return True
|
||||
return bool(old_tc.get("trigger_baseline_value") != new_tc.get("trigger_baseline_value"))
|
||||
|
||||
|
||||
def _migrate_flat_to_per_entity(config: dict[str, Any], first_entity_id: str) -> dict[str, dict[str, Any]]:
|
||||
"""Create ``_trigger_state`` from legacy flat keys on first load.
|
||||
|
||||
|
||||
@@ -227,6 +227,13 @@ def _build_export_object(
|
||||
# gallery) — only emitted when set, like the fleet flag below.
|
||||
if obj_data.get("template_id"):
|
||||
export_obj["template_id"] = obj_data["template_id"]
|
||||
# 2026-10 places — only when set, so an object at home exports as before.
|
||||
# A zone id is instance-specific: on another instance without that zone
|
||||
# the object behaves as at home and the panel says the zone is missing.
|
||||
if obj_data.get("place"):
|
||||
export_obj["place"] = obj_data["place"]
|
||||
if obj_data.get("remind_on_site") is True:
|
||||
export_obj["remind_on_site"] = True
|
||||
# Battery fleet identity — only emitted for the fleet object so a plain
|
||||
# object's export stays byte-identical to earlier versions. The importer
|
||||
# mirrors these (and keeps the deterministic ``batt_<type>`` part ids).
|
||||
|
||||
@@ -42,12 +42,13 @@ export interface MockFeatures {
|
||||
checklists: boolean;
|
||||
schedule_time: boolean;
|
||||
completion_actions: boolean;
|
||||
places: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_FEATURES: MockFeatures = {
|
||||
adaptive: false, seasonal: false,
|
||||
environmental: false, budget: false, groups: false,
|
||||
checklists: false, schedule_time: false, completion_actions: false,
|
||||
checklists: false, schedule_time: false, completion_actions: false, places: false,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -69,7 +69,7 @@ function ctx(overrides: Partial<TaskDetailContext> = {}): TaskDetailContext {
|
||||
features: {
|
||||
adaptive: false, seasonal: false,
|
||||
environmental: false, budget: false, groups: false,
|
||||
checklists: false, schedule_time: false, completion_actions: false,
|
||||
checklists: false, schedule_time: false, completion_actions: false, places: false,
|
||||
},
|
||||
currencySymbol: "€",
|
||||
collapsedSections: new Set(),
|
||||
@@ -297,7 +297,7 @@ describe("task-detail renderer", () => {
|
||||
features: {
|
||||
adaptive: false, seasonal: false,
|
||||
environmental: false, budget: false, groups: false,
|
||||
checklists: true, schedule_time: false, completion_actions: false,
|
||||
checklists: true, schedule_time: false, completion_actions: false, places: false,
|
||||
},
|
||||
setChecklistItem: (item, done) => calls.push([item, done]),
|
||||
}),
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ function ctx(overrides: Partial<TaskDetailContext> = {}): TaskDetailContext {
|
||||
features: {
|
||||
adaptive: false, seasonal: false,
|
||||
environmental: false, budget: false, groups: false,
|
||||
checklists: false, schedule_time: false, completion_actions: false,
|
||||
checklists: false, schedule_time: false, completion_actions: false, places: false,
|
||||
},
|
||||
currencySymbol: "€",
|
||||
collapsedSections: new Set(),
|
||||
|
||||
@@ -21,6 +21,8 @@ export class MaintenanceObjectDialog extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
/** All objects — choices for the parent-object picker (2.19). */
|
||||
@property({ attribute: false }) public objects: MaintenanceObjectResponse[] = [];
|
||||
/** 2026-10 places: the advanced feature switch shows the place fields. */
|
||||
@property({ type: Boolean }) public placesEnabled = false;
|
||||
@state() private _open = false;
|
||||
@state() private _loading = false;
|
||||
@state() private _error = "";
|
||||
@@ -39,6 +41,9 @@ export class MaintenanceObjectDialog extends LitElement {
|
||||
// 2.19: attach to an existing HA device / nest under another object
|
||||
@state() private _haDeviceId = "";
|
||||
@state() private _parentEntryId = "";
|
||||
// 2026-10 places: an HA zone ("" = home) and "remind only on site".
|
||||
@state() private _place = "";
|
||||
@state() private _remindOnSite = false;
|
||||
@state() private _entryId: string | null = null; // null = create, string = update
|
||||
// Replace (journey N1): the successor's device. The old unit's device is
|
||||
// what the object links to now; a new unit usually is a new device.
|
||||
@@ -63,6 +68,8 @@ export class MaintenanceObjectDialog extends LitElement {
|
||||
this._notes = "";
|
||||
this._haDeviceId = "";
|
||||
this._parentEntryId = "";
|
||||
this._place = "";
|
||||
this._remindOnSite = false;
|
||||
this._replacing = false;
|
||||
this._error = "";
|
||||
this._open = true;
|
||||
@@ -96,6 +103,8 @@ export class MaintenanceObjectDialog extends LitElement {
|
||||
this._notes = obj.notes || "";
|
||||
this._haDeviceId = obj.ha_device_id || "";
|
||||
this._parentEntryId = obj.parent_entry_id || "";
|
||||
this._place = obj.place || "";
|
||||
this._remindOnSite = !!obj.remind_on_site;
|
||||
this._replacing = false;
|
||||
this._error = "";
|
||||
this._open = true;
|
||||
@@ -194,6 +203,35 @@ export class MaintenanceObjectDialog extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
/** 2026-10 places: the zone the object is maintained at ("" = home) and
|
||||
* whether its reminders wait until somebody is there. */
|
||||
private _renderPlace(L: string) {
|
||||
const missing = !!this._place && !this.hass?.states?.[this._place];
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${{ place: this._place || undefined }}
|
||||
.schema=${[{ name: "place", selector: { entity: { domain: "zone" } } }]}
|
||||
.computeLabel=${() => t("place_optional", L)}
|
||||
@value-changed=${(e: CustomEvent) => {
|
||||
this._place = ((e.detail.value as { place?: string })?.place as string) || "";
|
||||
if (!this._place) this._remindOnSite = false;
|
||||
}}
|
||||
></ha-form>
|
||||
${missing ? html`<div class="place-missing">${t("place_missing", L)}</div>` : nothing}
|
||||
<label class="place-on-site ${this._place ? "" : "disabled"}">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${this._remindOnSite}
|
||||
?disabled=${!this._place}
|
||||
@change=${(e: Event) => (this._remindOnSite = (e.target as HTMLInputElement).checked)}
|
||||
/>
|
||||
<span>${t("remind_on_site", L)}</span>
|
||||
</label>
|
||||
${this._place ? html`<div class="place-hint">${t("remind_on_site_hint", L)}</div>` : nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
private async _save(): Promise<void> {
|
||||
if (this._loading) return; // synchronous re-entry guard (double-click)
|
||||
if (!this._name.trim()) return;
|
||||
@@ -211,6 +249,8 @@ export class MaintenanceObjectDialog extends LitElement {
|
||||
notes: this._notes.trim() || null,
|
||||
ha_device_id: this._haDeviceId || null,
|
||||
parent_entry_id: this._parentEntryId || null,
|
||||
// Only with the feature on: switched off, a stored place is kept as is.
|
||||
...(this.placesEnabled ? { place: this._place || null, remind_on_site: !!this._place && this._remindOnSite } : {}),
|
||||
};
|
||||
const res = await runWs<{ device_swap?: DeviceSwap }>(
|
||||
this,
|
||||
@@ -278,6 +318,7 @@ export class MaintenanceObjectDialog extends LitElement {
|
||||
@value-changed=${(e: CustomEvent) =>
|
||||
(this._areaId = (e.detail.value as string) || "")}
|
||||
></ha-area-picker>
|
||||
${this.placesEnabled ? this._renderPlace(L) : nothing}
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
@@ -352,6 +393,25 @@ export class MaintenanceObjectDialog extends LitElement {
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
.place-on-site {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.place-on-site.disabled {
|
||||
color: var(--disabled-text-color, var(--secondary-text-color));
|
||||
}
|
||||
.place-hint,
|
||||
.place-missing {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
margin-top: -4px;
|
||||
}
|
||||
.place-missing {
|
||||
color: var(--warning-color, #c77700);
|
||||
}
|
||||
.dialog-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
|
||||
@@ -703,6 +703,7 @@ export class MaintenanceSettingsView extends LitElement {
|
||||
{ key: "checklists", settingKey: "advanced_checklists_visible", label: t("feat_checklists", L), desc: t("feat_checklists_desc", L) },
|
||||
{ key: "schedule_time", settingKey: "advanced_schedule_time_visible", label: t("feat_schedule_time", L), desc: t("feat_schedule_time_desc", L) },
|
||||
{ key: "completion_actions", settingKey: "advanced_completion_actions_visible", label: t("feat_completion_actions", L), desc: t("feat_completion_actions_desc", L) },
|
||||
{ key: "places", settingKey: "advanced_places_visible", label: t("feat_places", L), desc: t("feat_places_desc", L) },
|
||||
];
|
||||
|
||||
return html`
|
||||
|
||||
@@ -28,6 +28,7 @@ import type { TriggerType } from "../types";
|
||||
import { REQUIRED_COMPLETION_KEYS, REQUIRED_COMPLETION_LABELS } from "./required-completion-labels";
|
||||
import "./ms-textfield";
|
||||
import { defaultNotifyIcon } from "../helpers/notify-icons";
|
||||
import { primaryTriggerEntity, triggerEntityIds } from "../helpers/trigger-entities";
|
||||
import "./ms-date-field";
|
||||
import "./ms-cost-input";
|
||||
|
||||
@@ -160,7 +161,7 @@ export function applyTypeFields(c: TriggerConfig, d: TriggerTypeFields): void {
|
||||
|
||||
/** Map a persisted compound condition (storage shape) to an editable draft. */
|
||||
function conditionToDraft(c: TriggerConfig): CompoundConditionDraft {
|
||||
const ids = c.entity_ids || (c.entity_id ? [c.entity_id] : []);
|
||||
const ids = triggerEntityIds(c);
|
||||
return {
|
||||
entityIds: ids.join(", "),
|
||||
...typeFieldsFromConfig(c),
|
||||
@@ -617,8 +618,8 @@ export class MaintenanceTaskDialog extends LitElement {
|
||||
// Fleet task) must still hydrate the singular field — the save path
|
||||
// gates on _triggerEntityId and would otherwise NULL the whole trigger
|
||||
// on an unrelated edit (issue #106).
|
||||
this._triggerEntityId = tc.entity_id || (tc.entity_ids && tc.entity_ids[0]) || "";
|
||||
this._triggerEntityIds = tc.entity_ids || (tc.entity_id ? [tc.entity_id] : []);
|
||||
this._triggerEntityId = primaryTriggerEntity(tc) || "";
|
||||
this._triggerEntityIds = triggerEntityIds(tc);
|
||||
this._triggerEntityLogic = tc.entity_logic || "any";
|
||||
this._setTypeFields(typeFieldsFromConfig(tc));
|
||||
this._triggerCombinator = tc.trigger_combinator === "all" ? "all" : "any";
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ export class MaintenanceTaskQuickActionsDialog extends LitElement {
|
||||
@state() private _features: AdvancedFeatures = {
|
||||
adaptive: false, seasonal: false, environmental: false,
|
||||
budget: false, groups: false, checklists: false, schedule_time: false,
|
||||
completion_actions: false,
|
||||
completion_actions: false, places: false,
|
||||
};
|
||||
@state() private _toast = "";
|
||||
private readonly _toastTimer = new ToastTimer();
|
||||
|
||||
@@ -129,9 +129,19 @@ export function openCreateObjectDialog(): boolean {
|
||||
if (!syncHass(dlg)) return false;
|
||||
// openCreate is defined on MaintenanceObjectDialog
|
||||
dlg.openCreate();
|
||||
void applyObjectDialogSettings(dlg);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 2026-10 places: the object dialog shows its place fields by the same
|
||||
* feature switch as in the panel. Opened at once; the fields appear when
|
||||
* the (page-wide cached) settings are in. */
|
||||
async function applyObjectDialogSettings(dlg: MaintenanceObjectDialog): Promise<void> {
|
||||
const hass = getHass();
|
||||
if (!hass) return;
|
||||
dlg.placesEnabled = (await fetchSettingsOnce(hass)).features.places;
|
||||
}
|
||||
|
||||
export function openEditObjectDialog(
|
||||
entryId: string,
|
||||
obj: MaintenanceObject,
|
||||
@@ -139,6 +149,7 @@ export function openEditObjectDialog(
|
||||
const dlg = getOrCreate<MaintenanceObjectDialog>(OBJECT_DIALOG_TAG);
|
||||
if (!syncHass(dlg)) return false;
|
||||
dlg.openEdit(entryId, obj);
|
||||
void applyObjectDialogSettings(dlg);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Zatím žádné objekty.",
|
||||
"action_error": "Akce se nezdařila. Zkuste to znovu.",
|
||||
"area_id_optional": "Oblast (volitelná)",
|
||||
"place_optional": "Místo (zóna, volitelné)",
|
||||
"remind_on_site": "Připomínat jen, když tam někdo je",
|
||||
"remind_on_site_hint": "Připomínky čekají, dokud někdo nebude v této zóně. Kdo tam přijede, dostane zprávu s tím, co je potřeba udělat.",
|
||||
"place_missing": "Zóna nenalezena: připomínky jako doma",
|
||||
"place_home": "Domov",
|
||||
"installation_date_optional": "Datum instalace (volitelné)",
|
||||
"warranty_expiry_optional": "Konec záruky (volitelné)",
|
||||
"warranty": "Záruka",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Vlastník",
|
||||
"feat_completion_actions": "Akce při dokončení",
|
||||
"feat_completion_actions_desc": "Akce HA na úlohu při dokončení + QR rychlého dokončení s předvolenými hodnotami.",
|
||||
"feat_places": "Místa",
|
||||
"feat_places_desc": "Objekty udržované na jiném místě (zóna Home Assistantu): připomínky jen když tam někdo je a zpráva při příjezdu.",
|
||||
"on_complete_action_title": "Při dokončení: spustit akci HA (volitelné)",
|
||||
"on_complete_action_desc": "Volá službu HA, když je úloha dokončena — např. resetuje čítač na zařízení.",
|
||||
"on_complete_action_target": "Cílová entita",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Používá díl: {name}",
|
||||
"label_filter": "Štítek",
|
||||
"all_labels": "Všechny štítky",
|
||||
"filter_place": "Místo",
|
||||
"filter_place_all": "Všechna místa",
|
||||
"settings_notify_scope": "Upozorňovat jen pro zobrazení",
|
||||
"settings_notify_scope_all": "Všechny úkoly",
|
||||
"settings_notify_scope_hint": "Připomenutí posílají jen úkoly odpovídající filtrům štítku/uživatele zvoleného zobrazení. Stav, řazení a seskupení zobrazení se zde ignorují.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Ingen objekter endnu.",
|
||||
"action_error": "Handlingen mislykkedes. Prøv igen.",
|
||||
"area_id_optional": "Område (valgfrit)",
|
||||
"place_optional": "Sted (zone, valgfrit)",
|
||||
"remind_on_site": "Påmind kun, når nogen er der",
|
||||
"remind_on_site_hint": "Påmindelser venter, til nogen er i denne zone. Den, der ankommer, får en besked med det, der skal gøres.",
|
||||
"place_missing": "Zone ikke fundet: påmindelser som hjemme",
|
||||
"place_home": "Hjemme",
|
||||
"installation_date_optional": "Installationsdato (valgfrit)",
|
||||
"warranty_expiry_optional": "Garantiens udløb (valgfrit)",
|
||||
"warranty": "Garanti",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "Ejer",
|
||||
"feat_completion_actions": "Fuldførelseshandlinger",
|
||||
"feat_completion_actions_desc": "HA-handling pr. opgave ved fuldførelse + hurtig-fuldfør-QR med forudindstillede værdier.",
|
||||
"feat_places": "Steder",
|
||||
"feat_places_desc": "Objekter, der vedligeholdes et andet sted (en Home Assistant-zone): påmindelser kun når nogen er der, og en besked ved ankomst.",
|
||||
"on_complete_action_title": "Ved fuldførelse: udløs HA-handling (valgfrit)",
|
||||
"on_complete_action_desc": "Kalder en HA-tjeneste, når opgaven fuldføres — f.eks. nulstil en tæller på enheden.",
|
||||
"on_complete_action_target": "Målenhed",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Bruger reservedel: {name}",
|
||||
"label_filter": "Etiket",
|
||||
"all_labels": "Alle etiketter",
|
||||
"filter_place": "Sted",
|
||||
"filter_place_all": "Alle steder",
|
||||
"settings_notify_scope": "Underret kun for visning",
|
||||
"settings_notify_scope_all": "Alle opgaver",
|
||||
"settings_notify_scope_hint": "Kun opgaver, der matcher den valgte visnings etiket-/brugerfiltre, sender påmindelser. Visningens status, sortering og gruppering ignoreres her.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Keine Objekte vorhanden.",
|
||||
"action_error": "Aktion fehlgeschlagen. Bitte erneut versuchen.",
|
||||
"area_id_optional": "Bereich (optional)",
|
||||
"place_optional": "Ort (Zone, optional)",
|
||||
"remind_on_site": "Nur erinnern, wenn jemand dort ist",
|
||||
"remind_on_site_hint": "Erinnerungen warten, bis jemand in dieser Zone ist. Wer dort ankommt, bekommt eine Nachricht mit dem, was fällig ist.",
|
||||
"place_missing": "Zone nicht gefunden: Erinnerungen wie zuhause",
|
||||
"place_home": "Zuhause",
|
||||
"installation_date_optional": "Installationsdatum (optional)",
|
||||
"warranty_expiry_optional": "Garantie-Ablauf (optional)",
|
||||
"warranty": "Garantie",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "Besitzer",
|
||||
"feat_completion_actions": "Completion-Actions",
|
||||
"feat_completion_actions_desc": "Pro Aufgabe HA-Action beim Abschluss konfigurieren + Quick-Complete-QR mit voreingestellten Werten.",
|
||||
"feat_places": "Orte",
|
||||
"feat_places_desc": "Objekte an einem anderen Ort (eine Home-Assistant-Zone): Erinnerungen nur, wenn jemand dort ist, und eine Nachricht bei Ankunft.",
|
||||
"on_complete_action_title": "Beim Abschluss: HA-Action auslösen (optional)",
|
||||
"on_complete_action_desc": "Ruft beim Erledigen der Aufgabe einen HA-Service auf — z. B. einen Zähler am Gerät zurücksetzen.",
|
||||
"on_complete_action_target": "Ziel-Entität",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Nutzt Ersatzteil: {name}",
|
||||
"label_filter": "Label",
|
||||
"all_labels": "Alle Labels",
|
||||
"filter_place": "Ort",
|
||||
"filter_place_all": "Alle Orte",
|
||||
"settings_notify_scope": "Nur benachrichtigen für Ansicht",
|
||||
"settings_notify_scope_all": "Alle Aufgaben",
|
||||
"settings_notify_scope_hint": "Nur Aufgaben, die zu den Label-/Nutzer-Filtern der gewählten Ansicht passen, senden Erinnerungen. Status, Sortierung und Gruppierung der Ansicht werden hier ignoriert.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "No objects yet.",
|
||||
"action_error": "Action failed. Please try again.",
|
||||
"area_id_optional": "Area (optional)",
|
||||
"place_optional": "Place (zone, optional)",
|
||||
"remind_on_site": "Remind only while somebody is there",
|
||||
"remind_on_site_hint": "Reminders wait until somebody is in this zone. Whoever arrives there gets a message with what is due.",
|
||||
"place_missing": "Zone not found: reminders as at home",
|
||||
"place_home": "Home",
|
||||
"installation_date_optional": "Installation date (optional)",
|
||||
"warranty_expiry_optional": "Warranty expiry (optional)",
|
||||
"warranty": "Warranty",
|
||||
@@ -813,6 +818,8 @@
|
||||
"owner_label": "Owner",
|
||||
"feat_completion_actions": "Completion actions",
|
||||
"feat_completion_actions_desc": "Per-task HA action on complete + quick-complete QR with pre-set values.",
|
||||
"feat_places": "Places",
|
||||
"feat_places_desc": "Objects maintained at another place (a Home Assistant zone): reminders only while somebody is there, and a message on arrival.",
|
||||
"on_complete_action_title": "On complete: trigger HA action (optional)",
|
||||
"on_complete_action_desc": "Calls an HA service when the task is completed — e.g. reset a counter on the device.",
|
||||
"on_complete_action_target": "Target entity",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Uses part: {name}",
|
||||
"label_filter": "Label",
|
||||
"all_labels": "All labels",
|
||||
"filter_place": "Place",
|
||||
"filter_place_all": "All places",
|
||||
"settings_notify_scope": "Notify only for view",
|
||||
"settings_notify_scope_all": "All tasks",
|
||||
"settings_notify_scope_hint": "Only tasks matching the selected saved view's label/user filters send reminders. Status, sorting and grouping of the view are ignored here.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Aún no hay objetos.",
|
||||
"action_error": "Acción fallida. Inténtelo de nuevo.",
|
||||
"area_id_optional": "Área (opcional)",
|
||||
"place_optional": "Lugar (zona, opcional)",
|
||||
"remind_on_site": "Recordar solo cuando alguien esté allí",
|
||||
"remind_on_site_hint": "Los recordatorios esperan a que alguien esté en esta zona. Quien llega allí recibe un mensaje con lo pendiente.",
|
||||
"place_missing": "Zona no encontrada: recordatorios como en casa",
|
||||
"place_home": "Casa",
|
||||
"installation_date_optional": "Fecha de instalación (opcional)",
|
||||
"warranty_expiry_optional": "Vencimiento de garantía (opcional)",
|
||||
"warranty": "Garantía",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Propietario",
|
||||
"feat_completion_actions": "Acciones de finalización",
|
||||
"feat_completion_actions_desc": "Acción HA por tarea al completar + QR de finalización rápida con valores prefijados.",
|
||||
"feat_places": "Lugares",
|
||||
"feat_places_desc": "Objetos mantenidos en otro lugar (una zona de Home Assistant): recordatorios solo cuando alguien está allí y un mensaje al llegar.",
|
||||
"on_complete_action_title": "Al completar: ejecutar acción HA (opcional)",
|
||||
"on_complete_action_desc": "Llama un servicio HA cuando se completa la tarea — p. ej. reiniciar un contador del dispositivo.",
|
||||
"on_complete_action_target": "Entidad objetivo",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Usa la pieza: {name}",
|
||||
"label_filter": "Etiqueta",
|
||||
"all_labels": "Todas las etiquetas",
|
||||
"filter_place": "Lugar",
|
||||
"filter_place_all": "Todos los lugares",
|
||||
"settings_notify_scope": "Notificar solo para la vista",
|
||||
"settings_notify_scope_all": "Todas las tareas",
|
||||
"settings_notify_scope_hint": "Solo las tareas que coinciden con los filtros de etiqueta/usuario de la vista seleccionada envían recordatorios. El estado, el orden y la agrupación de la vista se ignoran aquí.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Ei vielä kohteita.",
|
||||
"action_error": "Toiminto epäonnistui. Yritä uudelleen.",
|
||||
"area_id_optional": "Alue (valinnainen)",
|
||||
"place_optional": "Paikka (vyöhyke, valinnainen)",
|
||||
"remind_on_site": "Muistuta vain, kun joku on paikalla",
|
||||
"remind_on_site_hint": "Muistutukset odottavat, kunnes joku on tällä vyöhykkeellä. Sinne saapuva saa viestin tehtävistä asioista.",
|
||||
"place_missing": "Vyöhykettä ei löydy: muistutukset kuin kotona",
|
||||
"place_home": "Koti",
|
||||
"installation_date_optional": "Asennuspäivä (valinnainen)",
|
||||
"warranty_expiry_optional": "Takuun päättyminen (valinnainen)",
|
||||
"warranty": "Takuu",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "Omistaja",
|
||||
"feat_completion_actions": "Suoritustoiminnot",
|
||||
"feat_completion_actions_desc": "Tehtäväkohtainen HA-toiminto suorituksen yhteydessä + pikasuoritus-QR esiasetetuilla arvoilla.",
|
||||
"feat_places": "Paikat",
|
||||
"feat_places_desc": "Toisessa paikassa (Home Assistant -vyöhyke) huollettavat kohteet: muistutukset vain, kun joku on paikalla, ja viesti saavuttaessa.",
|
||||
"on_complete_action_title": "Suorituksen yhteydessä: laukaise HA-toiminto (valinnainen)",
|
||||
"on_complete_action_desc": "Kutsuu HA-palvelua, kun tehtävä suoritetaan — esim. nollaa laitteen laskuri.",
|
||||
"on_complete_action_target": "Kohde-entiteetti",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Käyttää varaosaa: {name}",
|
||||
"label_filter": "Tunniste",
|
||||
"all_labels": "Kaikki tunnisteet",
|
||||
"filter_place": "Paikka",
|
||||
"filter_place_all": "Kaikki paikat",
|
||||
"settings_notify_scope": "Ilmoita vain näkymälle",
|
||||
"settings_notify_scope_all": "Kaikki tehtävät",
|
||||
"settings_notify_scope_hint": "Vain valitun näkymän tunniste-/käyttäjäsuodattimiin osuvat tehtävät lähettävät muistutuksia. Näkymän tila, lajittelu ja ryhmittely ohitetaan tässä.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Aucun objet pour l'instant.",
|
||||
"action_error": "Action échouée. Veuillez réessayer.",
|
||||
"area_id_optional": "Zone (optionnel)",
|
||||
"place_optional": "Lieu (zone, facultatif)",
|
||||
"remind_on_site": "Rappeler seulement quand quelqu'un y est",
|
||||
"remind_on_site_hint": "Les rappels attendent que quelqu'un soit dans cette zone. Qui y arrive reçoit un message avec ce qui est à faire.",
|
||||
"place_missing": "Zone introuvable : rappels comme à la maison",
|
||||
"place_home": "Domicile",
|
||||
"installation_date_optional": "Date d'installation (optionnel)",
|
||||
"warranty_expiry_optional": "Fin de garantie (optionnel)",
|
||||
"warranty": "Garantie",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Propriétaire",
|
||||
"feat_completion_actions": "Actions de finalisation",
|
||||
"feat_completion_actions_desc": "Action HA par tâche lors de la finalisation + QR de finalisation rapide avec valeurs pré-définies.",
|
||||
"feat_places": "Lieux",
|
||||
"feat_places_desc": "Objets entretenus à un autre endroit (une zone Home Assistant) : rappels seulement quand quelqu'un y est, et un message à l'arrivée.",
|
||||
"on_complete_action_title": "À la finalisation : déclencher une action HA (optionnel)",
|
||||
"on_complete_action_desc": "Appelle un service HA quand la tâche est terminée — p. ex. réinitialiser un compteur sur l'appareil.",
|
||||
"on_complete_action_target": "Entité cible",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Utilise la pièce : {name}",
|
||||
"label_filter": "Étiquette",
|
||||
"all_labels": "Toutes les étiquettes",
|
||||
"filter_place": "Lieu",
|
||||
"filter_place_all": "Tous les lieux",
|
||||
"settings_notify_scope": "Notifier uniquement pour la vue",
|
||||
"settings_notify_scope_all": "Toutes les tâches",
|
||||
"settings_notify_scope_hint": "Seules les tâches correspondant aux filtres étiquette/utilisateur de la vue choisie envoient des rappels. Le statut, le tri et le regroupement de la vue sont ignorés ici.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "अभी कोई वस्तु नहीं।",
|
||||
"action_error": "क्रिया विफल। कृपया पुनः प्रयास करें।",
|
||||
"area_id_optional": "क्षेत्र (वैकल्पिक)",
|
||||
"place_optional": "स्थान (ज़ोन, वैकल्पिक)",
|
||||
"remind_on_site": "केवल तब याद दिलाएँ जब कोई वहाँ हो",
|
||||
"remind_on_site_hint": "रिमाइंडर तब तक रुकते हैं जब तक कोई इस ज़ोन में न हो। वहाँ पहुँचने वाले को बाकी कामों का संदेश मिलता है।",
|
||||
"place_missing": "ज़ोन नहीं मिला: रिमाइंडर घर जैसे",
|
||||
"place_home": "घर",
|
||||
"installation_date_optional": "स्थापना तिथि (वैकल्पिक)",
|
||||
"warranty_expiry_optional": "वारंटी समाप्ति (वैकल्पिक)",
|
||||
"warranty": "वारंटी",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "स्वामी",
|
||||
"feat_completion_actions": "पूर्णता क्रियाएँ",
|
||||
"feat_completion_actions_desc": "पूर्ण होने पर प्रति-कार्य HA क्रिया + पूर्व-निर्धारित मानों के साथ त्वरित-पूर्ण QR।",
|
||||
"feat_places": "स्थान",
|
||||
"feat_places_desc": "किसी दूसरी जगह (Home Assistant ज़ोन) पर रखरखाव वाले ऑब्जेक्ट: रिमाइंडर तभी जब कोई वहाँ हो, और पहुँचने पर एक संदेश।",
|
||||
"on_complete_action_title": "पूर्ण होने पर: HA क्रिया ट्रिगर करें (वैकल्पिक)",
|
||||
"on_complete_action_desc": "कार्य पूर्ण होने पर एक HA सेवा कॉल करता है — उदा. डिवाइस पर काउंटर रीसेट करना।",
|
||||
"on_complete_action_target": "लक्ष्य एंटिटी",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "पुर्ज़ा उपयोग करता है: {name}",
|
||||
"label_filter": "लेबल",
|
||||
"all_labels": "सभी लेबल",
|
||||
"filter_place": "स्थान",
|
||||
"filter_place_all": "सभी स्थान",
|
||||
"settings_notify_scope": "केवल इस व्यू के लिए सूचित करें",
|
||||
"settings_notify_scope_all": "सभी कार्य",
|
||||
"settings_notify_scope_hint": "केवल वे कार्य अनुस्मारक भेजते हैं जो चुने गए व्यू के लेबल/उपयोगकर्ता फ़िल्टर से मेल खाते हैं। व्यू की स्थिति, क्रम और समूहन यहाँ अनदेखे रहते हैं।",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Még nincsenek objektumok.",
|
||||
"action_error": "A művelet nem sikerült. Próbálja újra.",
|
||||
"area_id_optional": "Terület (opcionális)",
|
||||
"place_optional": "Helyszín (zóna, opcionális)",
|
||||
"remind_on_site": "Csak akkor emlékeztessen, ha valaki ott van",
|
||||
"remind_on_site_hint": "Az emlékeztetők megvárják, amíg valaki ebben a zónában lesz. Aki odaér, üzenetet kap az esedékes teendőkről.",
|
||||
"place_missing": "A zóna nem található: emlékeztetők, mint otthon",
|
||||
"place_home": "Otthon",
|
||||
"installation_date_optional": "Telepítés dátuma (opcionális)",
|
||||
"warranty_expiry_optional": "Garancia lejárta (opcionális)",
|
||||
"warranty": "Garancia",
|
||||
@@ -813,6 +818,8 @@
|
||||
"owner_label": "Tulajdonos",
|
||||
"feat_completion_actions": "Elvégzési műveletek",
|
||||
"feat_completion_actions_desc": "Feladatonkénti HA művelet elvégzéskor + gyorselvégző QR előre beállított értékekkel.",
|
||||
"feat_places": "Helyszínek",
|
||||
"feat_places_desc": "Másik helyszínen (Home Assistant-zóna) karbantartott objektumok: emlékeztető csak akkor, ha valaki ott van, és üzenet érkezéskor.",
|
||||
"on_complete_action_title": "Elvégzéskor: HA művelet indítása (opcionális)",
|
||||
"on_complete_action_desc": "HA szolgáltatást hív meg a feladat elvégzésekor — pl. számláló nullázása az eszközön.",
|
||||
"on_complete_action_target": "Cél entitás",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Alkatrészt használ: {name}",
|
||||
"label_filter": "Címke",
|
||||
"all_labels": "Minden címke",
|
||||
"filter_place": "Helyszín",
|
||||
"filter_place_all": "Minden helyszín",
|
||||
"settings_notify_scope": "Értesítés csak erre a nézetre",
|
||||
"settings_notify_scope_all": "Minden feladat",
|
||||
"settings_notify_scope_hint": "Csak a kiválasztott mentett nézet címke-/felhasználószűrőinek megfelelő feladatok küldenek emlékeztetőt. A nézet állapot-, rendezési és csoportosítási beállításait itt figyelmen kívül hagyjuk.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Nessun oggetto ancora.",
|
||||
"action_error": "Azione fallita. Riprova.",
|
||||
"area_id_optional": "Area (opzionale)",
|
||||
"place_optional": "Luogo (zona, facoltativo)",
|
||||
"remind_on_site": "Ricorda solo quando qualcuno è lì",
|
||||
"remind_on_site_hint": "I promemoria aspettano che qualcuno sia in questa zona. Chi arriva lì riceve un messaggio con ciò che è da fare.",
|
||||
"place_missing": "Zona non trovata: promemoria come a casa",
|
||||
"place_home": "Casa",
|
||||
"installation_date_optional": "Data di installazione (opzionale)",
|
||||
"warranty_expiry_optional": "Scadenza garanzia (opzionale)",
|
||||
"warranty": "Garanzia",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Proprietario",
|
||||
"feat_completion_actions": "Azioni di completamento",
|
||||
"feat_completion_actions_desc": "Azione HA per attività al completamento + QR completamento rapido con valori predefiniti.",
|
||||
"feat_places": "Luoghi",
|
||||
"feat_places_desc": "Oggetti curati in un altro luogo (una zona di Home Assistant): promemoria solo quando qualcuno è lì e un messaggio all'arrivo.",
|
||||
"on_complete_action_title": "Al completamento: attiva azione HA (opzionale)",
|
||||
"on_complete_action_desc": "Chiama un servizio HA quando l'attività viene completata — es. azzerare un contatore sul dispositivo.",
|
||||
"on_complete_action_target": "Entità target",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Usa il ricambio: {name}",
|
||||
"label_filter": "Etichetta",
|
||||
"all_labels": "Tutte le etichette",
|
||||
"filter_place": "Luogo",
|
||||
"filter_place_all": "Tutti i luoghi",
|
||||
"settings_notify_scope": "Notifica solo per la vista",
|
||||
"settings_notify_scope_all": "Tutte le attività",
|
||||
"settings_notify_scope_hint": "Solo le attività che corrispondono ai filtri etichetta/utente della vista scelta inviano promemoria. Stato, ordinamento e raggruppamento della vista qui vengono ignorati.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "対象がまだありません。",
|
||||
"action_error": "操作に失敗しました。もう一度お試しください。",
|
||||
"area_id_optional": "エリア(任意)",
|
||||
"place_optional": "場所(ゾーン、任意)",
|
||||
"remind_on_site": "誰かがいるときだけリマインド",
|
||||
"remind_on_site_hint": "リマインドは誰かがこのゾーンにいるまで待機します。到着した人には、やるべきことをまとめたメッセージが届きます。",
|
||||
"place_missing": "ゾーンが見つかりません:自宅と同じようにリマインドします",
|
||||
"place_home": "自宅",
|
||||
"installation_date_optional": "設置日(任意)",
|
||||
"warranty_expiry_optional": "保証期限(任意)",
|
||||
"warranty": "保証",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "オーナー",
|
||||
"feat_completion_actions": "完了時アクション",
|
||||
"feat_completion_actions_desc": "タスクごとの完了時HAアクション+事前設定値付きのクイック完了QR。",
|
||||
"feat_places": "場所",
|
||||
"feat_places_desc": "別の場所(Home Assistant のゾーン)で管理するオブジェクト:誰かがその場にいるときだけリマインドし、到着時に通知します。",
|
||||
"on_complete_action_title": "完了時: HAアクションを実行(任意)",
|
||||
"on_complete_action_desc": "タスク完了時にHAサービスを呼び出します — 例: デバイスのカウンターをリセット。",
|
||||
"on_complete_action_target": "対象エンティティ",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "使用する部品: {name}",
|
||||
"label_filter": "ラベル",
|
||||
"all_labels": "すべてのラベル",
|
||||
"filter_place": "場所",
|
||||
"filter_place_all": "すべての場所",
|
||||
"settings_notify_scope": "このビューのみ通知",
|
||||
"settings_notify_scope_all": "すべてのタスク",
|
||||
"settings_notify_scope_hint": "選択した保存済みビューのラベル/ユーザーフィルターに一致するタスクのみがリマインダーを送信します。ビューのステータス・並び順・グループ化はここでは無視されます。",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "아직 객체가 없습니다.",
|
||||
"action_error": "동작에 실패했습니다. 다시 시도해 주세요.",
|
||||
"area_id_optional": "구역 (선택)",
|
||||
"place_optional": "장소 (구역, 선택 사항)",
|
||||
"remind_on_site": "누군가 있을 때만 알림",
|
||||
"remind_on_site_hint": "누군가 이 구역에 있을 때까지 알림이 대기합니다. 그곳에 도착한 사람은 해야 할 일을 담은 메시지를 받습니다.",
|
||||
"place_missing": "구역을 찾을 수 없음: 집과 같이 알림",
|
||||
"place_home": "집",
|
||||
"installation_date_optional": "설치일 (선택)",
|
||||
"warranty_expiry_optional": "보증 만료일 (선택)",
|
||||
"warranty": "보증",
|
||||
@@ -813,6 +818,8 @@
|
||||
"owner_label": "소유자",
|
||||
"feat_completion_actions": "완료 동작",
|
||||
"feat_completion_actions_desc": "완료 시 작업별 HA 동작 실행 + 사전 설정값이 있는 빠른 완료 QR.",
|
||||
"feat_places": "장소",
|
||||
"feat_places_desc": "다른 장소(Home Assistant 구역)에서 관리하는 개체: 누군가 그곳에 있을 때만 알림을 보내고, 도착하면 메시지를 보냅니다.",
|
||||
"on_complete_action_title": "완료 시: HA 동작 실행 (선택)",
|
||||
"on_complete_action_desc": "작업이 완료되면 HA 서비스를 호출합니다 — 예: 기기의 카운터 초기화.",
|
||||
"on_complete_action_target": "대상 엔티티",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "사용 부품: {name}",
|
||||
"label_filter": "라벨",
|
||||
"all_labels": "모든 라벨",
|
||||
"filter_place": "장소",
|
||||
"filter_place_all": "모든 장소",
|
||||
"settings_notify_scope": "특정 보기만 알림",
|
||||
"settings_notify_scope_all": "모든 작업",
|
||||
"settings_notify_scope_hint": "선택한 저장된 보기의 라벨/사용자 필터와 일치하는 작업만 알림을 보냅니다. 보기의 상태·정렬·그룹화는 여기서 무시됩니다.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Ingen objekter ennå.",
|
||||
"action_error": "Handlingen mislyktes. Prøv igjen.",
|
||||
"area_id_optional": "Område (valgfritt)",
|
||||
"place_optional": "Sted (sone, valgfritt)",
|
||||
"remind_on_site": "Påminn bare når noen er der",
|
||||
"remind_on_site_hint": "Påminnelser venter til noen er i denne sonen. Den som kommer dit, får en melding med det som skal gjøres.",
|
||||
"place_missing": "Fant ikke sonen: påminnelser som hjemme",
|
||||
"place_home": "Hjemme",
|
||||
"installation_date_optional": "Installasjonsdato (valgfritt)",
|
||||
"warranty_expiry_optional": "Garanti utløper (valgfritt)",
|
||||
"warranty": "Garanti",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "Eier",
|
||||
"feat_completion_actions": "Fullføringshandlinger",
|
||||
"feat_completion_actions_desc": "HA-handling per oppgave ved fullføring + hurtigfullføring med QR og forhåndsinnstilte verdier.",
|
||||
"feat_places": "Steder",
|
||||
"feat_places_desc": "Objekter som vedlikeholdes et annet sted (en Home Assistant-sone): påminnelser bare når noen er der, og en melding ved ankomst.",
|
||||
"on_complete_action_title": "Ved fullføring: utløs HA-handling (valgfritt)",
|
||||
"on_complete_action_desc": "Kaller en HA-tjeneste når oppgaven fullføres — f.eks. tilbakestille en teller på enheten.",
|
||||
"on_complete_action_target": "Målentitet",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Bruker del: {name}",
|
||||
"label_filter": "Etikett",
|
||||
"all_labels": "Alle etiketter",
|
||||
"filter_place": "Sted",
|
||||
"filter_place_all": "Alle steder",
|
||||
"settings_notify_scope": "Varsle kun for visning",
|
||||
"settings_notify_scope_all": "Alle oppgaver",
|
||||
"settings_notify_scope_hint": "Bare oppgaver som matcher etikett-/brukerfiltrene til valgt visning sender påminnelser. Visningens status, sortering og gruppering ignoreres her.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Nog geen objecten.",
|
||||
"action_error": "Actie mislukt. Probeer het opnieuw.",
|
||||
"area_id_optional": "Gebied (optioneel)",
|
||||
"place_optional": "Locatie (zone, optioneel)",
|
||||
"remind_on_site": "Alleen herinneren als er iemand is",
|
||||
"remind_on_site_hint": "Herinneringen wachten tot iemand in deze zone is. Wie daar aankomt, krijgt een bericht met wat er te doen is.",
|
||||
"place_missing": "Zone niet gevonden: herinneringen zoals thuis",
|
||||
"place_home": "Thuis",
|
||||
"installation_date_optional": "Installatiedatum (optioneel)",
|
||||
"warranty_expiry_optional": "Garantie verloopt (optioneel)",
|
||||
"warranty": "Garantie",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Eigenaar",
|
||||
"feat_completion_actions": "Voltooiings-acties",
|
||||
"feat_completion_actions_desc": "Per taak HA-actie bij voltooien + snel-voltooien-QR met vooraf ingestelde waarden.",
|
||||
"feat_places": "Locaties",
|
||||
"feat_places_desc": "Objecten op een andere locatie (een Home Assistant-zone): herinneringen alleen als er iemand is, en een bericht bij aankomst.",
|
||||
"on_complete_action_title": "Bij voltooien: HA-actie uitvoeren (optioneel)",
|
||||
"on_complete_action_desc": "Roept een HA-service aan wanneer de taak is voltooid — bv. een teller op het apparaat resetten.",
|
||||
"on_complete_action_target": "Doel-entiteit",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Gebruikt onderdeel: {name}",
|
||||
"label_filter": "Label",
|
||||
"all_labels": "Alle labels",
|
||||
"filter_place": "Locatie",
|
||||
"filter_place_all": "Alle locaties",
|
||||
"settings_notify_scope": "Alleen melden voor weergave",
|
||||
"settings_notify_scope_all": "Alle taken",
|
||||
"settings_notify_scope_hint": "Alleen taken die overeenkomen met de label-/gebruikersfilters van de gekozen weergave sturen herinneringen. Status, sortering en groepering van de weergave worden hier genegeerd.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Brak obiektów.",
|
||||
"action_error": "Akcja nie powiodła się. Spróbuj ponownie.",
|
||||
"area_id_optional": "Obszar (opcjonalny)",
|
||||
"place_optional": "Miejsce (strefa, opcjonalnie)",
|
||||
"remind_on_site": "Przypominaj tylko, gdy ktoś tam jest",
|
||||
"remind_on_site_hint": "Przypomnienia czekają, aż ktoś będzie w tej strefie. Kto tam przyjedzie, dostaje wiadomość z tym, co jest do zrobienia.",
|
||||
"place_missing": "Nie znaleziono strefy: przypomnienia jak w domu",
|
||||
"place_home": "Dom",
|
||||
"installation_date_optional": "Data instalacji (opcjonalna)",
|
||||
"warranty_expiry_optional": "Wygaśnięcie gwarancji (opcjonalnie)",
|
||||
"warranty": "Gwarancja",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Właściciel",
|
||||
"feat_completion_actions": "Akcje po zakończeniu",
|
||||
"feat_completion_actions_desc": "Akcja HA per zadanie po zakończeniu + QR szybkiego zakończenia z predefiniowanymi wartościami.",
|
||||
"feat_places": "Miejsca",
|
||||
"feat_places_desc": "Obiekty w innym miejscu (strefa Home Assistant): przypomnienia tylko wtedy, gdy ktoś tam jest, i wiadomość po przybyciu.",
|
||||
"on_complete_action_title": "Po zakończeniu: wywołaj akcję HA (opcjonalnie)",
|
||||
"on_complete_action_desc": "Wywołuje usługę HA po zakończeniu zadania — np. zresetuj licznik urządzenia.",
|
||||
"on_complete_action_target": "Encja docelowa",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Używa części: {name}",
|
||||
"label_filter": "Etykieta",
|
||||
"all_labels": "Wszystkie etykiety",
|
||||
"filter_place": "Miejsce",
|
||||
"filter_place_all": "Wszystkie miejsca",
|
||||
"settings_notify_scope": "Powiadamiaj tylko dla widoku",
|
||||
"settings_notify_scope_all": "Wszystkie zadania",
|
||||
"settings_notify_scope_hint": "Przypomnienia wysyłają tylko zadania pasujące do filtrów etykiety/użytkownika wybranego widoku. Status, sortowanie i grupowanie widoku są tu ignorowane.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Ainda não há objetos.",
|
||||
"action_error": "A ação falhou. Tente novamente.",
|
||||
"area_id_optional": "Área (opcional)",
|
||||
"place_optional": "Local (zona, opcional)",
|
||||
"remind_on_site": "Lembrar só quando alguém estiver lá",
|
||||
"remind_on_site_hint": "Os lembretes esperam até alguém estar nesta zona. Quem chega lá recebe uma mensagem com o que está pendente.",
|
||||
"place_missing": "Zona não encontrada: lembretes como em casa",
|
||||
"place_home": "Casa",
|
||||
"installation_date_optional": "Data de instalação (opcional)",
|
||||
"warranty_expiry_optional": "Fim da garantia (opcional)",
|
||||
"warranty": "Garantia",
|
||||
@@ -813,6 +818,8 @@
|
||||
"owner_label": "Proprietário",
|
||||
"feat_completion_actions": "Ações de conclusão",
|
||||
"feat_completion_actions_desc": "Ação do HA por tarefa ao concluir + QR de conclusão rápida com valores predefinidos.",
|
||||
"feat_places": "Locais",
|
||||
"feat_places_desc": "Objetos mantidos em outro local (uma zona do Home Assistant): lembretes só quando alguém está lá e uma mensagem na chegada.",
|
||||
"on_complete_action_title": "Ao concluir: acionar ação do HA (opcional)",
|
||||
"on_complete_action_desc": "Chama um serviço do HA quando a tarefa é concluída — ex.: zerar um contador no dispositivo.",
|
||||
"on_complete_action_target": "Entidade alvo",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Usa a peça: {name}",
|
||||
"label_filter": "Etiqueta",
|
||||
"all_labels": "Todas as etiquetas",
|
||||
"filter_place": "Local",
|
||||
"filter_place_all": "Todos os locais",
|
||||
"settings_notify_scope": "Notificar apenas para a visualização",
|
||||
"settings_notify_scope_all": "Todas as tarefas",
|
||||
"settings_notify_scope_hint": "Apenas tarefas que correspondem aos filtros de etiqueta/usuário da visualização salva selecionada enviam lembretes. Status, ordenação e agrupamento da visualização são ignorados aqui.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Ainda sem objetos.",
|
||||
"action_error": "Ação falhada. Tente novamente.",
|
||||
"area_id_optional": "Área (opcional)",
|
||||
"place_optional": "Local (zona, opcional)",
|
||||
"remind_on_site": "Lembrar só quando alguém lá estiver",
|
||||
"remind_on_site_hint": "Os lembretes esperam até alguém estar nesta zona. Quem lá chega recebe uma mensagem com o que está pendente.",
|
||||
"place_missing": "Zona não encontrada: lembretes como em casa",
|
||||
"place_home": "Casa",
|
||||
"installation_date_optional": "Data de instalação (opcional)",
|
||||
"warranty_expiry_optional": "Validade da garantia (opcional)",
|
||||
"warranty": "Garantia",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Proprietário",
|
||||
"feat_completion_actions": "Ações de conclusão",
|
||||
"feat_completion_actions_desc": "Ação HA por tarefa ao concluir + QR de conclusão rápida com valores predefinidos.",
|
||||
"feat_places": "Locais",
|
||||
"feat_places_desc": "Objetos mantidos noutro local (uma zona do Home Assistant): lembretes só quando alguém lá está e uma mensagem à chegada.",
|
||||
"on_complete_action_title": "Na conclusão: acionar ação HA (opcional)",
|
||||
"on_complete_action_desc": "Chama um serviço HA quando a tarefa é concluída — p. ex. reiniciar um contador no dispositivo.",
|
||||
"on_complete_action_target": "Entidade alvo",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Usa a peça: {name}",
|
||||
"label_filter": "Etiqueta",
|
||||
"all_labels": "Todas as etiquetas",
|
||||
"filter_place": "Local",
|
||||
"filter_place_all": "Todos os locais",
|
||||
"settings_notify_scope": "Notificar apenas para a vista",
|
||||
"settings_notify_scope_all": "Todas as tarefas",
|
||||
"settings_notify_scope_hint": "Apenas as tarefas que correspondem aos filtros de etiqueta/utilizador da vista escolhida enviam lembretes. O estado, a ordenação e o agrupamento da vista são ignorados aqui.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Пока нет объектов.",
|
||||
"action_error": "Не удалось выполнить действие. Попробуйте ещё раз.",
|
||||
"area_id_optional": "Зона (опционально)",
|
||||
"place_optional": "Место (зона, необязательно)",
|
||||
"remind_on_site": "Напоминать, только когда там кто-то есть",
|
||||
"remind_on_site_hint": "Напоминания ждут, пока кто-нибудь окажется в этой зоне. Кто туда приезжает, получает сообщение о том, что нужно сделать.",
|
||||
"place_missing": "Зона не найдена: напоминания как дома",
|
||||
"place_home": "Дом",
|
||||
"installation_date_optional": "Дата установки (опционально)",
|
||||
"warranty_expiry_optional": "Окончание гарантии (опционально)",
|
||||
"warranty": "Гарантия",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Владелец",
|
||||
"feat_completion_actions": "Действия при завершении",
|
||||
"feat_completion_actions_desc": "HA-действие по задаче при завершении + QR быстрого завершения с предустановленными значениями.",
|
||||
"feat_places": "Места",
|
||||
"feat_places_desc": "Объекты в другом месте (зона Home Assistant): напоминания только когда там кто-то есть, и сообщение по прибытии.",
|
||||
"on_complete_action_title": "При завершении: запустить HA-действие (опционально)",
|
||||
"on_complete_action_desc": "Вызывает HA-сервис при завершении задачи — напр., сбросить счётчик на устройстве.",
|
||||
"on_complete_action_target": "Целевая сущность",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Использует деталь: {name}",
|
||||
"label_filter": "Метка",
|
||||
"all_labels": "Все метки",
|
||||
"filter_place": "Место",
|
||||
"filter_place_all": "Все места",
|
||||
"settings_notify_scope": "Уведомлять только для представления",
|
||||
"settings_notify_scope_all": "Все задачи",
|
||||
"settings_notify_scope_hint": "Напоминания отправляют только задачи, совпадающие с фильтрами метки/пользователя выбранного представления. Статус, сортировка и группировка представления здесь игнорируются.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Inga objekt än.",
|
||||
"action_error": "Åtgärden misslyckades. Försök igen.",
|
||||
"area_id_optional": "Område (valfritt)",
|
||||
"place_optional": "Plats (zon, valfritt)",
|
||||
"remind_on_site": "Påminn bara när någon är där",
|
||||
"remind_on_site_hint": "Påminnelser väntar tills någon är i den här zonen. Den som kommer dit får ett meddelande med det som ska göras.",
|
||||
"place_missing": "Zonen hittades inte: påminnelser som hemma",
|
||||
"place_home": "Hemma",
|
||||
"installation_date_optional": "Installationsdatum (valfritt)",
|
||||
"warranty_expiry_optional": "Garantins utgång (valfritt)",
|
||||
"warranty": "Garanti",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Ägare",
|
||||
"feat_completion_actions": "Slutförande-åtgärder",
|
||||
"feat_completion_actions_desc": "HA-åtgärd per uppgift vid slutförande + snabb-slutför-QR med förinställda värden.",
|
||||
"feat_places": "Platser",
|
||||
"feat_places_desc": "Objekt som sköts på en annan plats (en Home Assistant-zon): påminnelser bara när någon är där, och ett meddelande vid ankomst.",
|
||||
"on_complete_action_title": "Vid slutförande: utlös HA-åtgärd (valfritt)",
|
||||
"on_complete_action_desc": "Anropar en HA-tjänst när uppgiften slutförs — t.ex. återställ en räknare på enheten.",
|
||||
"on_complete_action_target": "Målentitet",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Använder reservdel: {name}",
|
||||
"label_filter": "Etikett",
|
||||
"all_labels": "Alla etiketter",
|
||||
"filter_place": "Plats",
|
||||
"filter_place_all": "Alla platser",
|
||||
"settings_notify_scope": "Avisera endast för vy",
|
||||
"settings_notify_scope_all": "Alla uppgifter",
|
||||
"settings_notify_scope_hint": "Endast uppgifter som matchar den valda vyns etikett-/användarfilter skickar påminnelser. Vyns status, sortering och gruppering ignoreras här.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Henüz nesne yok.",
|
||||
"action_error": "Eylem başarısız oldu. Lütfen tekrar deneyin.",
|
||||
"area_id_optional": "Alan (isteğe bağlı)",
|
||||
"place_optional": "Yer (bölge, isteğe bağlı)",
|
||||
"remind_on_site": "Yalnızca biri oradayken hatırlat",
|
||||
"remind_on_site_hint": "Hatırlatmalar biri bu bölgede olana kadar bekler. Oraya varan kişi yapılacakların listesini içeren bir mesaj alır.",
|
||||
"place_missing": "Bölge bulunamadı: hatırlatmalar evdeki gibi",
|
||||
"place_home": "Ev",
|
||||
"installation_date_optional": "Kurulum tarihi (isteğe bağlı)",
|
||||
"warranty_expiry_optional": "Garanti bitişi (isteğe bağlı)",
|
||||
"warranty": "Garanti",
|
||||
@@ -813,6 +818,8 @@
|
||||
"owner_label": "Sahip",
|
||||
"feat_completion_actions": "Tamamlama eylemleri",
|
||||
"feat_completion_actions_desc": "Görev başına tamamlamada HA eylemi + önceden ayarlı değerlerle hızlı tamamlama QR'ı.",
|
||||
"feat_places": "Yerler",
|
||||
"feat_places_desc": "Başka bir yerde (Home Assistant bölgesi) bakımı yapılan nesneler: hatırlatmalar yalnızca biri oradayken, varışta bir mesaj.",
|
||||
"on_complete_action_title": "Tamamlandığında: HA eylemi tetikle (isteğe bağlı)",
|
||||
"on_complete_action_desc": "Görev tamamlandığında bir HA servisi çağırır — örn. cihazdaki bir sayacı sıfırlar.",
|
||||
"on_complete_action_target": "Hedef varlık",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Kullanılan parça: {name}",
|
||||
"label_filter": "Etiket",
|
||||
"all_labels": "Tüm etiketler",
|
||||
"filter_place": "Yer",
|
||||
"filter_place_all": "Tüm yerler",
|
||||
"settings_notify_scope": "Yalnızca şu görünüm için bildir",
|
||||
"settings_notify_scope_all": "Tüm görevler",
|
||||
"settings_notify_scope_hint": "Yalnızca seçili kayıtlı görünümün etiket/kullanıcı filtreleriyle eşleşen görevler hatırlatma gönderir. Görünümün durum, sıralama ve gruplaması burada dikkate alınmaz.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Поки немає об'єктів.",
|
||||
"action_error": "Дія не вдалась. Спробуйте ще раз.",
|
||||
"area_id_optional": "Зона (необов'язково)",
|
||||
"place_optional": "Місце (зона, необов'язково)",
|
||||
"remind_on_site": "Нагадувати, лише коли там хтось є",
|
||||
"remind_on_site_hint": "Нагадування чекають, поки хтось буде в цій зоні. Хто туди прибуває, отримує повідомлення про те, що потрібно зробити.",
|
||||
"place_missing": "Зону не знайдено: нагадування як удома",
|
||||
"place_home": "Дім",
|
||||
"installation_date_optional": "Дата встановлення (необов'язково)",
|
||||
"warranty_expiry_optional": "Закінчення гарантії (необов'язково)",
|
||||
"warranty": "Гарантія",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Власник",
|
||||
"feat_completion_actions": "Дії при завершенні",
|
||||
"feat_completion_actions_desc": "Дія HA по завданню при завершенні + QR швидкого завершення з попередньо встановленими значеннями.",
|
||||
"feat_places": "Місця",
|
||||
"feat_places_desc": "Об'єкти в іншому місці (зона Home Assistant): нагадування лише тоді, коли там хтось є, і повідомлення після прибуття.",
|
||||
"on_complete_action_title": "При завершенні: викликати HA-дію (опційно)",
|
||||
"on_complete_action_desc": "Викликає HA-сервіс, коли завдання завершено — напр., скинути лічильник на пристрої.",
|
||||
"on_complete_action_target": "Цільова сутність",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Використовує деталь: {name}",
|
||||
"label_filter": "Мітка",
|
||||
"all_labels": "Усі мітки",
|
||||
"filter_place": "Місце",
|
||||
"filter_place_all": "Усі місця",
|
||||
"settings_notify_scope": "Сповіщати лише для подання",
|
||||
"settings_notify_scope_all": "Усі завдання",
|
||||
"settings_notify_scope_hint": "Нагадування надсилають лише завдання, що відповідають фільтрам мітки/користувача обраного подання. Статус, сортування та групування подання тут ігноруються.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "暂无维护项。",
|
||||
"action_error": "操作失败。请重试。",
|
||||
"area_id_optional": "区域 (可选)",
|
||||
"place_optional": "地点(区域,可选)",
|
||||
"remind_on_site": "仅当有人在场时提醒",
|
||||
"remind_on_site_hint": "提醒会等到有人在此区域时再发送。到达那里的人会收到一条列出待办事项的消息。",
|
||||
"place_missing": "未找到区域:按在家处理提醒",
|
||||
"place_home": "家",
|
||||
"installation_date_optional": "安装日期 (可选)",
|
||||
"warranty_expiry_optional": "保修到期 (可选)",
|
||||
"warranty": "保修",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "所有者",
|
||||
"feat_completion_actions": "完成动作",
|
||||
"feat_completion_actions_desc": "为每个任务配置完成时的 HA 动作 + 预设值的快速完成二维码。",
|
||||
"feat_places": "地点",
|
||||
"feat_places_desc": "在其他地点(Home Assistant 区域)维护的对象:仅当有人在场时提醒,并在到达时发送消息。",
|
||||
"on_complete_action_title": "完成时:触发 HA 动作(可选)",
|
||||
"on_complete_action_desc": "任务完成时调用 HA 服务 — 例如重置设备上的物理计数器。",
|
||||
"on_complete_action_target": "目标实体",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "使用配件:{name}",
|
||||
"label_filter": "标签",
|
||||
"all_labels": "所有标签",
|
||||
"filter_place": "地点",
|
||||
"filter_place_all": "所有地点",
|
||||
"settings_notify_scope": "仅为该视图发送通知",
|
||||
"settings_notify_scope_all": "所有任务",
|
||||
"settings_notify_scope_hint": "只有匹配所选视图的标签/用户筛选的任务才会发送提醒。视图的状态、排序和分组在此处被忽略。",
|
||||
|
||||
@@ -131,6 +131,7 @@ import type { PartsCostMode } from "./helpers/parts-cost";
|
||||
import { canWrite } from "./helpers/permissions";
|
||||
import { renderStatusBadge } from "./renderers/status";
|
||||
import { TOAST_MS, ACTION_TOAST_MS } from "./helpers/toast";
|
||||
import { primaryTriggerEntity } from "./helpers/trigger-entities";
|
||||
import { buildHistoryEntryDraft } from "./helpers/history-draft";
|
||||
import { readingSlotDelta } from "./helpers/reading-slots";
|
||||
import { bulkResultMessage, codedFailures, runWs, runWsEach } from "./helpers/ws-run";
|
||||
@@ -236,6 +237,8 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
@state() private _filterStatus = "";
|
||||
@state() private _filterUser: string | null = null;
|
||||
@state() private _filterLabel: string | null = null;
|
||||
// 2026-10 places: "" = all, "home" = objects without a place, else a zone id.
|
||||
@state() private _filterPlace = "";
|
||||
/** #134: only tasks of this priority (low|normal|high); "" = no filter. */
|
||||
@state() private _filterPriority = "";
|
||||
// v2.24: shared saved filter views + the id of the one currently applied ("" =
|
||||
@@ -264,7 +267,7 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
@state() private _groups: Record<string, MaintenanceGroup> = {};
|
||||
@state() private _detailStatsData: Map<string, StatisticsPoint[]> = new Map();
|
||||
@state() private _miniStatsData: Map<string, StatisticsPoint[]> = new Map();
|
||||
@state() private _features: AdvancedFeatures = { adaptive: false, seasonal: false, environmental: false, budget: false, groups: false, checklists: false, schedule_time: false, completion_actions: false };
|
||||
@state() private _features: AdvancedFeatures = { adaptive: false, seasonal: false, environmental: false, budget: false, groups: false, checklists: false, schedule_time: false, completion_actions: false, places: false };
|
||||
// HA user IDs (UUIDs) granted full panel access despite not being HA admins.
|
||||
@state() private _adminPanelUserIds: string[] = [];
|
||||
// v2.8.4: master switch — the allowlist only grants the full panel when this
|
||||
@@ -1087,7 +1090,7 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
const task = this._selectedEntryId && this._selectedTaskId
|
||||
? this._getTask(this._selectedEntryId, this._selectedTaskId)
|
||||
: null;
|
||||
const entityId = task?.trigger_config?.entity_id;
|
||||
const entityId = primaryTriggerEntity(task?.trigger_config);
|
||||
if (entityId) {
|
||||
// Drop the stale-range series so the chart shows its loading state.
|
||||
const updated = new Map(this._detailStatsData);
|
||||
@@ -1111,7 +1114,8 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
const entities: Array<{ entityId: string; isCounter: boolean }> = [];
|
||||
for (const obj of this._objects) {
|
||||
for (const task of obj.tasks) {
|
||||
const entityId = task.trigger_config?.entity_id;
|
||||
// The battery fleet's row shows "N to replace", not a sparkline.
|
||||
const entityId = task.battery_fleet_task ? undefined : primaryTriggerEntity(task.trigger_config);
|
||||
if (!entityId) continue;
|
||||
entities.push({ entityId, isCounter: this._isCounterEntity(task.trigger_config) });
|
||||
}
|
||||
@@ -1217,6 +1221,9 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
// Label filter (v2.26 — also captured by saved views)
|
||||
if (this._filterLabel && !(task.labels || []).includes(this._filterLabel)) continue;
|
||||
|
||||
// Place filter (2026-10): the object's zone, "home" for none.
|
||||
if (this._filterPlace && (obj.object.place || "home") !== this._filterPlace) continue;
|
||||
|
||||
// Priority filter (#134 — also captured by saved views). Tasks
|
||||
// without an explicit priority are "normal", the model default.
|
||||
if (this._filterPriority && (task.priority || "normal") !== this._filterPriority) continue;
|
||||
@@ -1353,8 +1360,9 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
if (s.msp_view === "task" && s.msp_entry && s.msp_task) {
|
||||
this._historyFilter = null;
|
||||
const task = this._getTask(s.msp_entry, s.msp_task);
|
||||
if (task?.trigger_config?.entity_id) {
|
||||
this._fetchDetailStats(task.trigger_config.entity_id, this._isCounterEntity(task.trigger_config));
|
||||
const entityId = primaryTriggerEntity(task?.trigger_config);
|
||||
if (task && entityId) {
|
||||
this._fetchDetailStats(entityId, this._isCounterEntity(task.trigger_config));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1454,6 +1462,16 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
return [...seen].sort((a, b) => a.localeCompare(b));
|
||||
}
|
||||
|
||||
/** 2026-10 places: the zones objects are maintained at, for the filter. */
|
||||
private get _allPlaces(): { id: string; name: string }[] {
|
||||
const seen = new Map<string, string>();
|
||||
for (const obj of this._objects) {
|
||||
const o = obj.object;
|
||||
if (o.place && !seen.has(o.place)) seen.set(o.place, o.place_name || o.place);
|
||||
}
|
||||
return [...seen].map(([id, name]) => ({ id, name })).sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
/** The panel's current task-list filter state, in the shape a view stores. */
|
||||
private get _currentFilters(): SavedViewFilters {
|
||||
return {
|
||||
@@ -1615,8 +1633,9 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
this._resetStickyPane();
|
||||
this._fetchFullHistory(entryId, taskId);
|
||||
const task = this._getTask(entryId, taskId);
|
||||
if (task?.trigger_config?.entity_id) {
|
||||
this._fetchDetailStats(task.trigger_config.entity_id, this._isCounterEntity(task.trigger_config));
|
||||
const entityId = primaryTriggerEntity(task?.trigger_config);
|
||||
if (task && entityId) {
|
||||
this._fetchDetailStats(entityId, this._isCounterEntity(task.trigger_config));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1637,10 +1656,9 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
|
||||
// Lazy-load statistics for the task's trigger entity
|
||||
const task = this._getTask(entryId, taskId);
|
||||
if (task?.trigger_config?.entity_id) {
|
||||
const entityId = task.trigger_config.entity_id;
|
||||
const isCounter = this._isCounterEntity(task.trigger_config);
|
||||
this._fetchDetailStats(entityId, isCounter);
|
||||
const entityId = primaryTriggerEntity(task?.trigger_config);
|
||||
if (task && entityId) {
|
||||
this._fetchDetailStats(entityId, this._isCounterEntity(task.trigger_config));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3196,6 +3214,7 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
<maintenance-object-dialog
|
||||
.hass=${this.hass}
|
||||
.objects=${this._objects}
|
||||
.placesEnabled=${this._features.places}
|
||||
@object-saved=${this._onObjectSaved}
|
||||
@object-replaced=${this._onObjectReplaced}
|
||||
></maintenance-object-dialog>
|
||||
@@ -3543,6 +3562,7 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
(this._filterStatus ? 1 : 0) +
|
||||
(this._filterUser ? 1 : 0) +
|
||||
(this._filterLabel ? 1 : 0) +
|
||||
(this._filterPlace ? 1 : 0) +
|
||||
(this._filterPriority ? 1 : 0) +
|
||||
(this._activeViewId ? 1 : 0);
|
||||
|
||||
@@ -3626,6 +3646,24 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
</select>
|
||||
</label>
|
||||
` : nothing}
|
||||
${this._features.places && this._allPlaces.length > 0 ? html`
|
||||
<label class="filter-field">
|
||||
<span class="filter-label">${t("filter_place", L)}</span>
|
||||
<select
|
||||
.value=${this._filterPlace}
|
||||
@change=${(e: Event) => {
|
||||
this._filterPlace = (e.target as HTMLSelectElement).value;
|
||||
this._activeViewId = "";
|
||||
}}
|
||||
>
|
||||
<option value="">${t("filter_place_all", L)}</option>
|
||||
<option value="home" ?selected=${this._filterPlace === "home"}>${t("place_home", L)}</option>
|
||||
${this._allPlaces.map(
|
||||
(pl) => html`<option value=${pl.id} ?selected=${this._filterPlace === pl.id}>${pl.name}</option>`
|
||||
)}
|
||||
</select>
|
||||
</label>
|
||||
` : nothing}
|
||||
<label class="filter-field">
|
||||
<span class="filter-label">${t("priority", L)}</span>
|
||||
<select
|
||||
@@ -4743,6 +4781,13 @@ export class MaintenanceSupporterPanel extends LitElement {
|
||||
<a href="#" class="object-area-link" @click=${(e: Event) => { e.preventDefault(); this._showArea(o.area_id!); }}
|
||||
>${areaDisplayName(o.area_id, this.hass?.areas, t("no_area", L))}</a></p>`
|
||||
: nothing}
|
||||
${o.place
|
||||
? html`<p class="meta place-meta">
|
||||
<ha-icon icon="mdi:map-marker-outline"></ha-icon>
|
||||
${t("filter_place", L)}: ${o.place_name || o.place}${o.remind_on_site ? html` · ${t("remind_on_site", L)}` : nothing}
|
||||
${o.place_missing ? html`<span class="place-missing-badge">${t("place_missing", L)}</span>` : nothing}
|
||||
</p>`
|
||||
: nothing}
|
||||
${isSafeHttpUrl(o.documentation_url)
|
||||
? html`<p class="meta">${t("documentation_url_label", L)}:
|
||||
<a href=${o.documentation_url} target="_blank" rel="noopener noreferrer">${o.documentation_url}</a>
|
||||
|
||||
@@ -963,6 +963,14 @@ export const panelStyles = css`
|
||||
color: var(--info-color, #2196f3); font-weight: 500;
|
||||
}
|
||||
.paused-meta ha-icon { --mdc-icon-size: 16px; }
|
||||
/* 2026-10 places: the object's place on its detail page. */
|
||||
.place-meta {
|
||||
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
|
||||
}
|
||||
.place-meta ha-icon { --mdc-icon-size: 16px; color: var(--secondary-text-color); }
|
||||
.place-missing-badge {
|
||||
font-size: 12px; color: var(--warning-color, #c77700);
|
||||
}
|
||||
.objects-table .oc-notes {
|
||||
max-width: 220px;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -16,6 +16,7 @@ import { t, formatDate, formatDueDays, formatNumber } from "../styles";
|
||||
import { px } from "./chart-utils";
|
||||
import { daysProgress } from "../helpers/interval";
|
||||
import { stampMs } from "../helpers/ha-time";
|
||||
import { primaryTriggerEntity } from "../helpers/trigger-entities";
|
||||
import type { MaintenanceTask, TaskRow, StatisticsPoint } from "../types";
|
||||
|
||||
const MINI_SPARKLINE_W = 60;
|
||||
@@ -36,7 +37,9 @@ export function computeTrend(
|
||||
miniStatsData: Map<string, StatisticsPoint[]>,
|
||||
): TrendState | null {
|
||||
const tc = row.trigger_config;
|
||||
if (!tc?.entity_id) return null;
|
||||
const entityId = primaryTriggerEntity(tc);
|
||||
// The battery fleet's row tells "N to replace", not one battery's curve.
|
||||
if (!tc || !entityId || row.battery_fleet_task) return null;
|
||||
let toward: 1 | -1;
|
||||
const type = tc.type || "threshold";
|
||||
if (type === "threshold") {
|
||||
@@ -48,7 +51,7 @@ export function computeTrend(
|
||||
} else {
|
||||
return null; // compound: conditions may point both ways
|
||||
}
|
||||
const statsPoints = miniStatsData.get(tc.entity_id) || [];
|
||||
const statsPoints = miniStatsData.get(entityId) || [];
|
||||
// Runtime and state_change accumulate SINCE the last completion — the
|
||||
// history's trigger_value is the count at the previous trigger, a point
|
||||
// from before the reset. Falling back to it read a freshly reset task
|
||||
@@ -235,8 +238,9 @@ export function renderMiniSparkline(
|
||||
miniStatsData: Map<string, StatisticsPoint[]>,
|
||||
lang: string,
|
||||
) {
|
||||
if (!row.trigger_config?.entity_id) return nothing;
|
||||
const entityId = row.trigger_config.entity_id;
|
||||
const entityId = primaryTriggerEntity(row.trigger_config);
|
||||
// The battery fleet's row tells "N to replace", not one battery's curve.
|
||||
if (!entityId || row.battery_fleet_task) return nothing;
|
||||
|
||||
// PRIMARY: HA recorder statistics (daily, last 14 days)
|
||||
const statsPoints = miniStatsData.get(entityId) || [];
|
||||
|
||||
@@ -16,6 +16,7 @@ import { t, fireMoreInfo, formatDueDays } from "../styles";
|
||||
import { fmtNum, fmtVal } from "./chart-utils";
|
||||
import { dueDaysOf } from "./progress";
|
||||
import { stampMs } from "../helpers/ha-time";
|
||||
import { primaryTriggerEntity, triggerEntityIds } from "../helpers/trigger-entities";
|
||||
import "../components/trigger-chart";
|
||||
import type { ChartPoint, ChartEvent } from "../components/trigger-chart";
|
||||
import type { MaintenanceTask, TriggerConfig, StatisticsPoint } from "../types";
|
||||
@@ -156,9 +157,9 @@ export function renderTriggerSection(task: MaintenanceTask, ctx: SparklineContex
|
||||
const L = ctx.lang;
|
||||
const info = task.trigger_entity_info;
|
||||
const infos = task.trigger_entity_infos;
|
||||
const friendlyName = info?.friendly_name || tc.entity_id || "—";
|
||||
const entityId = tc.entity_id || "";
|
||||
const entityIds = tc.entity_ids || (entityId ? [entityId] : []);
|
||||
const entityId = primaryTriggerEntity(tc) || "";
|
||||
const friendlyName = info?.friendly_name || entityId || "—";
|
||||
const entityIds = triggerEntityIds(tc);
|
||||
const unit = info?.unit_of_measurement || "";
|
||||
const currentVal = task.trigger_current_value;
|
||||
const triggerType = tc.type || "threshold";
|
||||
@@ -215,7 +216,7 @@ export function renderTriggerSection(task: MaintenanceTask, ctx: SparklineContex
|
||||
${triggerType === "compound" ? html`
|
||||
<span class="trigger-limit-item"><span class="dot warn" aria-hidden="true"></span> ${t("compound_logic", L)}: ${tc.compound_logic || (tc as any).operator || "AND"}</span>
|
||||
${(tc.conditions || []).map((cond: any, i: number) => html`
|
||||
<span class="trigger-limit-item"><span class="dot range" aria-hidden="true"></span> ${i + 1}. ${t(cond.type === "due_date" ? "trigger_type_due_date" : cond.type || "unknown", L)}: ${cond.entity_id ? html`<span class="entity-link" @click=${(ev: Event) => fireMoreInfo(ev, cond.entity_id)}>${cond.entity_id}</span>` : ""}</span>
|
||||
<span class="trigger-limit-item"><span class="dot range" aria-hidden="true"></span> ${i + 1}. ${t(cond.type === "due_date" ? "trigger_type_due_date" : cond.type || "unknown", L)}: ${triggerEntityIds(cond).map((eid, j) => html`${j > 0 ? ", " : ""}<span class="entity-link" @click=${(ev: Event) => fireMoreInfo(ev, eid)}>${eid}</span>`)}</span>
|
||||
`)}
|
||||
` : nothing}
|
||||
</div>
|
||||
@@ -330,7 +331,7 @@ function rawStatsPoints(task: MaintenanceTask, ctx: SparklineContext): ChartPoin
|
||||
const tc = task.trigger_config;
|
||||
if (!tc) return [];
|
||||
const triggerType = tc.type || "threshold";
|
||||
const entityId = tc.entity_id || "";
|
||||
const entityId = primaryTriggerEntity(tc) || "";
|
||||
// Runtime accumulates hours DERIVED from the entity's on/off time — the
|
||||
// entity's own long-term statistics (an on/off ratio, or an unrelated raw
|
||||
// sensor value) are NOT that accumulation. Plot only the recorded per-cycle
|
||||
@@ -372,7 +373,7 @@ function renderChart(task: MaintenanceTask, unit: string, ctx: SparklineContext)
|
||||
const tc = task.trigger_config;
|
||||
if (!tc) return nothing;
|
||||
const triggerType = tc.type || "threshold";
|
||||
const entityId = tc.entity_id || "";
|
||||
const entityId = primaryTriggerEntity(tc) || "";
|
||||
// A due date only counts down — no curve to tell.
|
||||
if (triggerType === "due_date") return nothing;
|
||||
|
||||
|
||||
@@ -40,6 +40,13 @@ export interface MaintenanceObject {
|
||||
// 2.19: attach to an existing HA device / nest under another object
|
||||
ha_device_id?: string | null;
|
||||
parent_entry_id?: string | null;
|
||||
/** 2026-10 places: an HA zone the object is maintained at (null = home),
|
||||
* its name, whether the zone is gone (then: as at home), and whether the
|
||||
* object's reminders wait until somebody is there. */
|
||||
place?: string | null;
|
||||
place_name?: string | null;
|
||||
place_missing?: boolean;
|
||||
remind_on_site?: boolean;
|
||||
/** v1.4.0 (#43): optional link to PDF manual / vendor page for the object */
|
||||
documentation_url?: string | null;
|
||||
/** v1.4.10 (#46): free-form notes — part numbers, procedures, etc. */
|
||||
@@ -550,6 +557,8 @@ export interface AdvancedFeatures {
|
||||
schedule_time: boolean;
|
||||
/** v1.3.0: gates per-task on_complete_action + quick_complete_defaults UI. */
|
||||
completion_actions: boolean;
|
||||
/** 2026-10: an object's place (an HA zone) and "remind only on site". */
|
||||
places: boolean;
|
||||
}
|
||||
|
||||
/** A single point in a recorder statistics time series. */
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Zatím žádné objekty.",
|
||||
"action_error": "Akce se nezdařila. Zkuste to znovu.",
|
||||
"area_id_optional": "Oblast (volitelná)",
|
||||
"place_optional": "Místo (zóna, volitelné)",
|
||||
"remind_on_site": "Připomínat jen, když tam někdo je",
|
||||
"remind_on_site_hint": "Připomínky čekají, dokud někdo nebude v této zóně. Kdo tam přijede, dostane zprávu s tím, co je potřeba udělat.",
|
||||
"place_missing": "Zóna nenalezena: připomínky jako doma",
|
||||
"place_home": "Domov",
|
||||
"installation_date_optional": "Datum instalace (volitelné)",
|
||||
"warranty_expiry_optional": "Konec záruky (volitelné)",
|
||||
"warranty": "Záruka",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Vlastník",
|
||||
"feat_completion_actions": "Akce při dokončení",
|
||||
"feat_completion_actions_desc": "Akce HA na úlohu při dokončení + QR rychlého dokončení s předvolenými hodnotami.",
|
||||
"feat_places": "Místa",
|
||||
"feat_places_desc": "Objekty udržované na jiném místě (zóna Home Assistantu): připomínky jen když tam někdo je a zpráva při příjezdu.",
|
||||
"on_complete_action_title": "Při dokončení: spustit akci HA (volitelné)",
|
||||
"on_complete_action_desc": "Volá službu HA, když je úloha dokončena — např. resetuje čítač na zařízení.",
|
||||
"on_complete_action_target": "Cílová entita",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Používá díl: {name}",
|
||||
"label_filter": "Štítek",
|
||||
"all_labels": "Všechny štítky",
|
||||
"filter_place": "Místo",
|
||||
"filter_place_all": "Všechna místa",
|
||||
"settings_notify_scope": "Upozorňovat jen pro zobrazení",
|
||||
"settings_notify_scope_all": "Všechny úkoly",
|
||||
"settings_notify_scope_hint": "Připomenutí posílají jen úkoly odpovídající filtrům štítku/uživatele zvoleného zobrazení. Stav, řazení a seskupení zobrazení se zde ignorují.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Ingen objekter endnu.",
|
||||
"action_error": "Handlingen mislykkedes. Prøv igen.",
|
||||
"area_id_optional": "Område (valgfrit)",
|
||||
"place_optional": "Sted (zone, valgfrit)",
|
||||
"remind_on_site": "Påmind kun, når nogen er der",
|
||||
"remind_on_site_hint": "Påmindelser venter, til nogen er i denne zone. Den, der ankommer, får en besked med det, der skal gøres.",
|
||||
"place_missing": "Zone ikke fundet: påmindelser som hjemme",
|
||||
"place_home": "Hjemme",
|
||||
"installation_date_optional": "Installationsdato (valgfrit)",
|
||||
"warranty_expiry_optional": "Garantiens udløb (valgfrit)",
|
||||
"warranty": "Garanti",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "Ejer",
|
||||
"feat_completion_actions": "Fuldførelseshandlinger",
|
||||
"feat_completion_actions_desc": "HA-handling pr. opgave ved fuldførelse + hurtig-fuldfør-QR med forudindstillede værdier.",
|
||||
"feat_places": "Steder",
|
||||
"feat_places_desc": "Objekter, der vedligeholdes et andet sted (en Home Assistant-zone): påmindelser kun når nogen er der, og en besked ved ankomst.",
|
||||
"on_complete_action_title": "Ved fuldførelse: udløs HA-handling (valgfrit)",
|
||||
"on_complete_action_desc": "Kalder en HA-tjeneste, når opgaven fuldføres — f.eks. nulstil en tæller på enheden.",
|
||||
"on_complete_action_target": "Målenhed",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Bruger reservedel: {name}",
|
||||
"label_filter": "Etiket",
|
||||
"all_labels": "Alle etiketter",
|
||||
"filter_place": "Sted",
|
||||
"filter_place_all": "Alle steder",
|
||||
"settings_notify_scope": "Underret kun for visning",
|
||||
"settings_notify_scope_all": "Alle opgaver",
|
||||
"settings_notify_scope_hint": "Kun opgaver, der matcher den valgte visnings etiket-/brugerfiltre, sender påmindelser. Visningens status, sortering og gruppering ignoreres her.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Keine Objekte vorhanden.",
|
||||
"action_error": "Aktion fehlgeschlagen. Bitte erneut versuchen.",
|
||||
"area_id_optional": "Bereich (optional)",
|
||||
"place_optional": "Ort (Zone, optional)",
|
||||
"remind_on_site": "Nur erinnern, wenn jemand dort ist",
|
||||
"remind_on_site_hint": "Erinnerungen warten, bis jemand in dieser Zone ist. Wer dort ankommt, bekommt eine Nachricht mit dem, was fällig ist.",
|
||||
"place_missing": "Zone nicht gefunden: Erinnerungen wie zuhause",
|
||||
"place_home": "Zuhause",
|
||||
"installation_date_optional": "Installationsdatum (optional)",
|
||||
"warranty_expiry_optional": "Garantie-Ablauf (optional)",
|
||||
"warranty": "Garantie",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "Besitzer",
|
||||
"feat_completion_actions": "Completion-Actions",
|
||||
"feat_completion_actions_desc": "Pro Aufgabe HA-Action beim Abschluss konfigurieren + Quick-Complete-QR mit voreingestellten Werten.",
|
||||
"feat_places": "Orte",
|
||||
"feat_places_desc": "Objekte an einem anderen Ort (eine Home-Assistant-Zone): Erinnerungen nur, wenn jemand dort ist, und eine Nachricht bei Ankunft.",
|
||||
"on_complete_action_title": "Beim Abschluss: HA-Action auslösen (optional)",
|
||||
"on_complete_action_desc": "Ruft beim Erledigen der Aufgabe einen HA-Service auf — z. B. einen Zähler am Gerät zurücksetzen.",
|
||||
"on_complete_action_target": "Ziel-Entität",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Nutzt Ersatzteil: {name}",
|
||||
"label_filter": "Label",
|
||||
"all_labels": "Alle Labels",
|
||||
"filter_place": "Ort",
|
||||
"filter_place_all": "Alle Orte",
|
||||
"settings_notify_scope": "Nur benachrichtigen für Ansicht",
|
||||
"settings_notify_scope_all": "Alle Aufgaben",
|
||||
"settings_notify_scope_hint": "Nur Aufgaben, die zu den Label-/Nutzer-Filtern der gewählten Ansicht passen, senden Erinnerungen. Status, Sortierung und Gruppierung der Ansicht werden hier ignoriert.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "No objects yet.",
|
||||
"action_error": "Action failed. Please try again.",
|
||||
"area_id_optional": "Area (optional)",
|
||||
"place_optional": "Place (zone, optional)",
|
||||
"remind_on_site": "Remind only while somebody is there",
|
||||
"remind_on_site_hint": "Reminders wait until somebody is in this zone. Whoever arrives there gets a message with what is due.",
|
||||
"place_missing": "Zone not found: reminders as at home",
|
||||
"place_home": "Home",
|
||||
"installation_date_optional": "Installation date (optional)",
|
||||
"warranty_expiry_optional": "Warranty expiry (optional)",
|
||||
"warranty": "Warranty",
|
||||
@@ -813,6 +818,8 @@
|
||||
"owner_label": "Owner",
|
||||
"feat_completion_actions": "Completion actions",
|
||||
"feat_completion_actions_desc": "Per-task HA action on complete + quick-complete QR with pre-set values.",
|
||||
"feat_places": "Places",
|
||||
"feat_places_desc": "Objects maintained at another place (a Home Assistant zone): reminders only while somebody is there, and a message on arrival.",
|
||||
"on_complete_action_title": "On complete: trigger HA action (optional)",
|
||||
"on_complete_action_desc": "Calls an HA service when the task is completed — e.g. reset a counter on the device.",
|
||||
"on_complete_action_target": "Target entity",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Uses part: {name}",
|
||||
"label_filter": "Label",
|
||||
"all_labels": "All labels",
|
||||
"filter_place": "Place",
|
||||
"filter_place_all": "All places",
|
||||
"settings_notify_scope": "Notify only for view",
|
||||
"settings_notify_scope_all": "All tasks",
|
||||
"settings_notify_scope_hint": "Only tasks matching the selected saved view's label/user filters send reminders. Status, sorting and grouping of the view are ignored here.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Aún no hay objetos.",
|
||||
"action_error": "Acción fallida. Inténtelo de nuevo.",
|
||||
"area_id_optional": "Área (opcional)",
|
||||
"place_optional": "Lugar (zona, opcional)",
|
||||
"remind_on_site": "Recordar solo cuando alguien esté allí",
|
||||
"remind_on_site_hint": "Los recordatorios esperan a que alguien esté en esta zona. Quien llega allí recibe un mensaje con lo pendiente.",
|
||||
"place_missing": "Zona no encontrada: recordatorios como en casa",
|
||||
"place_home": "Casa",
|
||||
"installation_date_optional": "Fecha de instalación (opcional)",
|
||||
"warranty_expiry_optional": "Vencimiento de garantía (opcional)",
|
||||
"warranty": "Garantía",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Propietario",
|
||||
"feat_completion_actions": "Acciones de finalización",
|
||||
"feat_completion_actions_desc": "Acción HA por tarea al completar + QR de finalización rápida con valores prefijados.",
|
||||
"feat_places": "Lugares",
|
||||
"feat_places_desc": "Objetos mantenidos en otro lugar (una zona de Home Assistant): recordatorios solo cuando alguien está allí y un mensaje al llegar.",
|
||||
"on_complete_action_title": "Al completar: ejecutar acción HA (opcional)",
|
||||
"on_complete_action_desc": "Llama un servicio HA cuando se completa la tarea — p. ej. reiniciar un contador del dispositivo.",
|
||||
"on_complete_action_target": "Entidad objetivo",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Usa la pieza: {name}",
|
||||
"label_filter": "Etiqueta",
|
||||
"all_labels": "Todas las etiquetas",
|
||||
"filter_place": "Lugar",
|
||||
"filter_place_all": "Todos los lugares",
|
||||
"settings_notify_scope": "Notificar solo para la vista",
|
||||
"settings_notify_scope_all": "Todas las tareas",
|
||||
"settings_notify_scope_hint": "Solo las tareas que coinciden con los filtros de etiqueta/usuario de la vista seleccionada envían recordatorios. El estado, el orden y la agrupación de la vista se ignoran aquí.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Ei vielä kohteita.",
|
||||
"action_error": "Toiminto epäonnistui. Yritä uudelleen.",
|
||||
"area_id_optional": "Alue (valinnainen)",
|
||||
"place_optional": "Paikka (vyöhyke, valinnainen)",
|
||||
"remind_on_site": "Muistuta vain, kun joku on paikalla",
|
||||
"remind_on_site_hint": "Muistutukset odottavat, kunnes joku on tällä vyöhykkeellä. Sinne saapuva saa viestin tehtävistä asioista.",
|
||||
"place_missing": "Vyöhykettä ei löydy: muistutukset kuin kotona",
|
||||
"place_home": "Koti",
|
||||
"installation_date_optional": "Asennuspäivä (valinnainen)",
|
||||
"warranty_expiry_optional": "Takuun päättyminen (valinnainen)",
|
||||
"warranty": "Takuu",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "Omistaja",
|
||||
"feat_completion_actions": "Suoritustoiminnot",
|
||||
"feat_completion_actions_desc": "Tehtäväkohtainen HA-toiminto suorituksen yhteydessä + pikasuoritus-QR esiasetetuilla arvoilla.",
|
||||
"feat_places": "Paikat",
|
||||
"feat_places_desc": "Toisessa paikassa (Home Assistant -vyöhyke) huollettavat kohteet: muistutukset vain, kun joku on paikalla, ja viesti saavuttaessa.",
|
||||
"on_complete_action_title": "Suorituksen yhteydessä: laukaise HA-toiminto (valinnainen)",
|
||||
"on_complete_action_desc": "Kutsuu HA-palvelua, kun tehtävä suoritetaan — esim. nollaa laitteen laskuri.",
|
||||
"on_complete_action_target": "Kohde-entiteetti",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Käyttää varaosaa: {name}",
|
||||
"label_filter": "Tunniste",
|
||||
"all_labels": "Kaikki tunnisteet",
|
||||
"filter_place": "Paikka",
|
||||
"filter_place_all": "Kaikki paikat",
|
||||
"settings_notify_scope": "Ilmoita vain näkymälle",
|
||||
"settings_notify_scope_all": "Kaikki tehtävät",
|
||||
"settings_notify_scope_hint": "Vain valitun näkymän tunniste-/käyttäjäsuodattimiin osuvat tehtävät lähettävät muistutuksia. Näkymän tila, lajittelu ja ryhmittely ohitetaan tässä.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Aucun objet pour l'instant.",
|
||||
"action_error": "Action échouée. Veuillez réessayer.",
|
||||
"area_id_optional": "Zone (optionnel)",
|
||||
"place_optional": "Lieu (zone, facultatif)",
|
||||
"remind_on_site": "Rappeler seulement quand quelqu'un y est",
|
||||
"remind_on_site_hint": "Les rappels attendent que quelqu'un soit dans cette zone. Qui y arrive reçoit un message avec ce qui est à faire.",
|
||||
"place_missing": "Zone introuvable : rappels comme à la maison",
|
||||
"place_home": "Domicile",
|
||||
"installation_date_optional": "Date d'installation (optionnel)",
|
||||
"warranty_expiry_optional": "Fin de garantie (optionnel)",
|
||||
"warranty": "Garantie",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Propriétaire",
|
||||
"feat_completion_actions": "Actions de finalisation",
|
||||
"feat_completion_actions_desc": "Action HA par tâche lors de la finalisation + QR de finalisation rapide avec valeurs pré-définies.",
|
||||
"feat_places": "Lieux",
|
||||
"feat_places_desc": "Objets entretenus à un autre endroit (une zone Home Assistant) : rappels seulement quand quelqu'un y est, et un message à l'arrivée.",
|
||||
"on_complete_action_title": "À la finalisation : déclencher une action HA (optionnel)",
|
||||
"on_complete_action_desc": "Appelle un service HA quand la tâche est terminée — p. ex. réinitialiser un compteur sur l'appareil.",
|
||||
"on_complete_action_target": "Entité cible",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Utilise la pièce : {name}",
|
||||
"label_filter": "Étiquette",
|
||||
"all_labels": "Toutes les étiquettes",
|
||||
"filter_place": "Lieu",
|
||||
"filter_place_all": "Tous les lieux",
|
||||
"settings_notify_scope": "Notifier uniquement pour la vue",
|
||||
"settings_notify_scope_all": "Toutes les tâches",
|
||||
"settings_notify_scope_hint": "Seules les tâches correspondant aux filtres étiquette/utilisateur de la vue choisie envoient des rappels. Le statut, le tri et le regroupement de la vue sont ignorés ici.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "अभी कोई वस्तु नहीं।",
|
||||
"action_error": "क्रिया विफल। कृपया पुनः प्रयास करें।",
|
||||
"area_id_optional": "क्षेत्र (वैकल्पिक)",
|
||||
"place_optional": "स्थान (ज़ोन, वैकल्पिक)",
|
||||
"remind_on_site": "केवल तब याद दिलाएँ जब कोई वहाँ हो",
|
||||
"remind_on_site_hint": "रिमाइंडर तब तक रुकते हैं जब तक कोई इस ज़ोन में न हो। वहाँ पहुँचने वाले को बाकी कामों का संदेश मिलता है।",
|
||||
"place_missing": "ज़ोन नहीं मिला: रिमाइंडर घर जैसे",
|
||||
"place_home": "घर",
|
||||
"installation_date_optional": "स्थापना तिथि (वैकल्पिक)",
|
||||
"warranty_expiry_optional": "वारंटी समाप्ति (वैकल्पिक)",
|
||||
"warranty": "वारंटी",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "स्वामी",
|
||||
"feat_completion_actions": "पूर्णता क्रियाएँ",
|
||||
"feat_completion_actions_desc": "पूर्ण होने पर प्रति-कार्य HA क्रिया + पूर्व-निर्धारित मानों के साथ त्वरित-पूर्ण QR।",
|
||||
"feat_places": "स्थान",
|
||||
"feat_places_desc": "किसी दूसरी जगह (Home Assistant ज़ोन) पर रखरखाव वाले ऑब्जेक्ट: रिमाइंडर तभी जब कोई वहाँ हो, और पहुँचने पर एक संदेश।",
|
||||
"on_complete_action_title": "पूर्ण होने पर: HA क्रिया ट्रिगर करें (वैकल्पिक)",
|
||||
"on_complete_action_desc": "कार्य पूर्ण होने पर एक HA सेवा कॉल करता है — उदा. डिवाइस पर काउंटर रीसेट करना।",
|
||||
"on_complete_action_target": "लक्ष्य एंटिटी",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "पुर्ज़ा उपयोग करता है: {name}",
|
||||
"label_filter": "लेबल",
|
||||
"all_labels": "सभी लेबल",
|
||||
"filter_place": "स्थान",
|
||||
"filter_place_all": "सभी स्थान",
|
||||
"settings_notify_scope": "केवल इस व्यू के लिए सूचित करें",
|
||||
"settings_notify_scope_all": "सभी कार्य",
|
||||
"settings_notify_scope_hint": "केवल वे कार्य अनुस्मारक भेजते हैं जो चुने गए व्यू के लेबल/उपयोगकर्ता फ़िल्टर से मेल खाते हैं। व्यू की स्थिति, क्रम और समूहन यहाँ अनदेखे रहते हैं।",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Még nincsenek objektumok.",
|
||||
"action_error": "A művelet nem sikerült. Próbálja újra.",
|
||||
"area_id_optional": "Terület (opcionális)",
|
||||
"place_optional": "Helyszín (zóna, opcionális)",
|
||||
"remind_on_site": "Csak akkor emlékeztessen, ha valaki ott van",
|
||||
"remind_on_site_hint": "Az emlékeztetők megvárják, amíg valaki ebben a zónában lesz. Aki odaér, üzenetet kap az esedékes teendőkről.",
|
||||
"place_missing": "A zóna nem található: emlékeztetők, mint otthon",
|
||||
"place_home": "Otthon",
|
||||
"installation_date_optional": "Telepítés dátuma (opcionális)",
|
||||
"warranty_expiry_optional": "Garancia lejárta (opcionális)",
|
||||
"warranty": "Garancia",
|
||||
@@ -813,6 +818,8 @@
|
||||
"owner_label": "Tulajdonos",
|
||||
"feat_completion_actions": "Elvégzési műveletek",
|
||||
"feat_completion_actions_desc": "Feladatonkénti HA művelet elvégzéskor + gyorselvégző QR előre beállított értékekkel.",
|
||||
"feat_places": "Helyszínek",
|
||||
"feat_places_desc": "Másik helyszínen (Home Assistant-zóna) karbantartott objektumok: emlékeztető csak akkor, ha valaki ott van, és üzenet érkezéskor.",
|
||||
"on_complete_action_title": "Elvégzéskor: HA művelet indítása (opcionális)",
|
||||
"on_complete_action_desc": "HA szolgáltatást hív meg a feladat elvégzésekor — pl. számláló nullázása az eszközön.",
|
||||
"on_complete_action_target": "Cél entitás",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Alkatrészt használ: {name}",
|
||||
"label_filter": "Címke",
|
||||
"all_labels": "Minden címke",
|
||||
"filter_place": "Helyszín",
|
||||
"filter_place_all": "Minden helyszín",
|
||||
"settings_notify_scope": "Értesítés csak erre a nézetre",
|
||||
"settings_notify_scope_all": "Minden feladat",
|
||||
"settings_notify_scope_hint": "Csak a kiválasztott mentett nézet címke-/felhasználószűrőinek megfelelő feladatok küldenek emlékeztetőt. A nézet állapot-, rendezési és csoportosítási beállításait itt figyelmen kívül hagyjuk.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Nessun oggetto ancora.",
|
||||
"action_error": "Azione fallita. Riprova.",
|
||||
"area_id_optional": "Area (opzionale)",
|
||||
"place_optional": "Luogo (zona, facoltativo)",
|
||||
"remind_on_site": "Ricorda solo quando qualcuno è lì",
|
||||
"remind_on_site_hint": "I promemoria aspettano che qualcuno sia in questa zona. Chi arriva lì riceve un messaggio con ciò che è da fare.",
|
||||
"place_missing": "Zona non trovata: promemoria come a casa",
|
||||
"place_home": "Casa",
|
||||
"installation_date_optional": "Data di installazione (opzionale)",
|
||||
"warranty_expiry_optional": "Scadenza garanzia (opzionale)",
|
||||
"warranty": "Garanzia",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Proprietario",
|
||||
"feat_completion_actions": "Azioni di completamento",
|
||||
"feat_completion_actions_desc": "Azione HA per attività al completamento + QR completamento rapido con valori predefiniti.",
|
||||
"feat_places": "Luoghi",
|
||||
"feat_places_desc": "Oggetti curati in un altro luogo (una zona di Home Assistant): promemoria solo quando qualcuno è lì e un messaggio all'arrivo.",
|
||||
"on_complete_action_title": "Al completamento: attiva azione HA (opzionale)",
|
||||
"on_complete_action_desc": "Chiama un servizio HA quando l'attività viene completata — es. azzerare un contatore sul dispositivo.",
|
||||
"on_complete_action_target": "Entità target",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Usa il ricambio: {name}",
|
||||
"label_filter": "Etichetta",
|
||||
"all_labels": "Tutte le etichette",
|
||||
"filter_place": "Luogo",
|
||||
"filter_place_all": "Tutti i luoghi",
|
||||
"settings_notify_scope": "Notifica solo per la vista",
|
||||
"settings_notify_scope_all": "Tutte le attività",
|
||||
"settings_notify_scope_hint": "Solo le attività che corrispondono ai filtri etichetta/utente della vista scelta inviano promemoria. Stato, ordinamento e raggruppamento della vista qui vengono ignorati.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "対象がまだありません。",
|
||||
"action_error": "操作に失敗しました。もう一度お試しください。",
|
||||
"area_id_optional": "エリア(任意)",
|
||||
"place_optional": "場所(ゾーン、任意)",
|
||||
"remind_on_site": "誰かがいるときだけリマインド",
|
||||
"remind_on_site_hint": "リマインドは誰かがこのゾーンにいるまで待機します。到着した人には、やるべきことをまとめたメッセージが届きます。",
|
||||
"place_missing": "ゾーンが見つかりません:自宅と同じようにリマインドします",
|
||||
"place_home": "自宅",
|
||||
"installation_date_optional": "設置日(任意)",
|
||||
"warranty_expiry_optional": "保証期限(任意)",
|
||||
"warranty": "保証",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "オーナー",
|
||||
"feat_completion_actions": "完了時アクション",
|
||||
"feat_completion_actions_desc": "タスクごとの完了時HAアクション+事前設定値付きのクイック完了QR。",
|
||||
"feat_places": "場所",
|
||||
"feat_places_desc": "別の場所(Home Assistant のゾーン)で管理するオブジェクト:誰かがその場にいるときだけリマインドし、到着時に通知します。",
|
||||
"on_complete_action_title": "完了時: HAアクションを実行(任意)",
|
||||
"on_complete_action_desc": "タスク完了時にHAサービスを呼び出します — 例: デバイスのカウンターをリセット。",
|
||||
"on_complete_action_target": "対象エンティティ",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "使用する部品: {name}",
|
||||
"label_filter": "ラベル",
|
||||
"all_labels": "すべてのラベル",
|
||||
"filter_place": "場所",
|
||||
"filter_place_all": "すべての場所",
|
||||
"settings_notify_scope": "このビューのみ通知",
|
||||
"settings_notify_scope_all": "すべてのタスク",
|
||||
"settings_notify_scope_hint": "選択した保存済みビューのラベル/ユーザーフィルターに一致するタスクのみがリマインダーを送信します。ビューのステータス・並び順・グループ化はここでは無視されます。",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "아직 객체가 없습니다.",
|
||||
"action_error": "동작에 실패했습니다. 다시 시도해 주세요.",
|
||||
"area_id_optional": "구역 (선택)",
|
||||
"place_optional": "장소 (구역, 선택 사항)",
|
||||
"remind_on_site": "누군가 있을 때만 알림",
|
||||
"remind_on_site_hint": "누군가 이 구역에 있을 때까지 알림이 대기합니다. 그곳에 도착한 사람은 해야 할 일을 담은 메시지를 받습니다.",
|
||||
"place_missing": "구역을 찾을 수 없음: 집과 같이 알림",
|
||||
"place_home": "집",
|
||||
"installation_date_optional": "설치일 (선택)",
|
||||
"warranty_expiry_optional": "보증 만료일 (선택)",
|
||||
"warranty": "보증",
|
||||
@@ -813,6 +818,8 @@
|
||||
"owner_label": "소유자",
|
||||
"feat_completion_actions": "완료 동작",
|
||||
"feat_completion_actions_desc": "완료 시 작업별 HA 동작 실행 + 사전 설정값이 있는 빠른 완료 QR.",
|
||||
"feat_places": "장소",
|
||||
"feat_places_desc": "다른 장소(Home Assistant 구역)에서 관리하는 개체: 누군가 그곳에 있을 때만 알림을 보내고, 도착하면 메시지를 보냅니다.",
|
||||
"on_complete_action_title": "완료 시: HA 동작 실행 (선택)",
|
||||
"on_complete_action_desc": "작업이 완료되면 HA 서비스를 호출합니다 — 예: 기기의 카운터 초기화.",
|
||||
"on_complete_action_target": "대상 엔티티",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "사용 부품: {name}",
|
||||
"label_filter": "라벨",
|
||||
"all_labels": "모든 라벨",
|
||||
"filter_place": "장소",
|
||||
"filter_place_all": "모든 장소",
|
||||
"settings_notify_scope": "특정 보기만 알림",
|
||||
"settings_notify_scope_all": "모든 작업",
|
||||
"settings_notify_scope_hint": "선택한 저장된 보기의 라벨/사용자 필터와 일치하는 작업만 알림을 보냅니다. 보기의 상태·정렬·그룹화는 여기서 무시됩니다.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Ingen objekter ennå.",
|
||||
"action_error": "Handlingen mislyktes. Prøv igjen.",
|
||||
"area_id_optional": "Område (valgfritt)",
|
||||
"place_optional": "Sted (sone, valgfritt)",
|
||||
"remind_on_site": "Påminn bare når noen er der",
|
||||
"remind_on_site_hint": "Påminnelser venter til noen er i denne sonen. Den som kommer dit, får en melding med det som skal gjøres.",
|
||||
"place_missing": "Fant ikke sonen: påminnelser som hjemme",
|
||||
"place_home": "Hjemme",
|
||||
"installation_date_optional": "Installasjonsdato (valgfritt)",
|
||||
"warranty_expiry_optional": "Garanti utløper (valgfritt)",
|
||||
"warranty": "Garanti",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "Eier",
|
||||
"feat_completion_actions": "Fullføringshandlinger",
|
||||
"feat_completion_actions_desc": "HA-handling per oppgave ved fullføring + hurtigfullføring med QR og forhåndsinnstilte verdier.",
|
||||
"feat_places": "Steder",
|
||||
"feat_places_desc": "Objekter som vedlikeholdes et annet sted (en Home Assistant-sone): påminnelser bare når noen er der, og en melding ved ankomst.",
|
||||
"on_complete_action_title": "Ved fullføring: utløs HA-handling (valgfritt)",
|
||||
"on_complete_action_desc": "Kaller en HA-tjeneste når oppgaven fullføres — f.eks. tilbakestille en teller på enheten.",
|
||||
"on_complete_action_target": "Målentitet",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Bruker del: {name}",
|
||||
"label_filter": "Etikett",
|
||||
"all_labels": "Alle etiketter",
|
||||
"filter_place": "Sted",
|
||||
"filter_place_all": "Alle steder",
|
||||
"settings_notify_scope": "Varsle kun for visning",
|
||||
"settings_notify_scope_all": "Alle oppgaver",
|
||||
"settings_notify_scope_hint": "Bare oppgaver som matcher etikett-/brukerfiltrene til valgt visning sender påminnelser. Visningens status, sortering og gruppering ignoreres her.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Nog geen objecten.",
|
||||
"action_error": "Actie mislukt. Probeer het opnieuw.",
|
||||
"area_id_optional": "Gebied (optioneel)",
|
||||
"place_optional": "Locatie (zone, optioneel)",
|
||||
"remind_on_site": "Alleen herinneren als er iemand is",
|
||||
"remind_on_site_hint": "Herinneringen wachten tot iemand in deze zone is. Wie daar aankomt, krijgt een bericht met wat er te doen is.",
|
||||
"place_missing": "Zone niet gevonden: herinneringen zoals thuis",
|
||||
"place_home": "Thuis",
|
||||
"installation_date_optional": "Installatiedatum (optioneel)",
|
||||
"warranty_expiry_optional": "Garantie verloopt (optioneel)",
|
||||
"warranty": "Garantie",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Eigenaar",
|
||||
"feat_completion_actions": "Voltooiings-acties",
|
||||
"feat_completion_actions_desc": "Per taak HA-actie bij voltooien + snel-voltooien-QR met vooraf ingestelde waarden.",
|
||||
"feat_places": "Locaties",
|
||||
"feat_places_desc": "Objecten op een andere locatie (een Home Assistant-zone): herinneringen alleen als er iemand is, en een bericht bij aankomst.",
|
||||
"on_complete_action_title": "Bij voltooien: HA-actie uitvoeren (optioneel)",
|
||||
"on_complete_action_desc": "Roept een HA-service aan wanneer de taak is voltooid — bv. een teller op het apparaat resetten.",
|
||||
"on_complete_action_target": "Doel-entiteit",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Gebruikt onderdeel: {name}",
|
||||
"label_filter": "Label",
|
||||
"all_labels": "Alle labels",
|
||||
"filter_place": "Locatie",
|
||||
"filter_place_all": "Alle locaties",
|
||||
"settings_notify_scope": "Alleen melden voor weergave",
|
||||
"settings_notify_scope_all": "Alle taken",
|
||||
"settings_notify_scope_hint": "Alleen taken die overeenkomen met de label-/gebruikersfilters van de gekozen weergave sturen herinneringen. Status, sortering en groepering van de weergave worden hier genegeerd.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Brak obiektów.",
|
||||
"action_error": "Akcja nie powiodła się. Spróbuj ponownie.",
|
||||
"area_id_optional": "Obszar (opcjonalny)",
|
||||
"place_optional": "Miejsce (strefa, opcjonalnie)",
|
||||
"remind_on_site": "Przypominaj tylko, gdy ktoś tam jest",
|
||||
"remind_on_site_hint": "Przypomnienia czekają, aż ktoś będzie w tej strefie. Kto tam przyjedzie, dostaje wiadomość z tym, co jest do zrobienia.",
|
||||
"place_missing": "Nie znaleziono strefy: przypomnienia jak w domu",
|
||||
"place_home": "Dom",
|
||||
"installation_date_optional": "Data instalacji (opcjonalna)",
|
||||
"warranty_expiry_optional": "Wygaśnięcie gwarancji (opcjonalnie)",
|
||||
"warranty": "Gwarancja",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Właściciel",
|
||||
"feat_completion_actions": "Akcje po zakończeniu",
|
||||
"feat_completion_actions_desc": "Akcja HA per zadanie po zakończeniu + QR szybkiego zakończenia z predefiniowanymi wartościami.",
|
||||
"feat_places": "Miejsca",
|
||||
"feat_places_desc": "Obiekty w innym miejscu (strefa Home Assistant): przypomnienia tylko wtedy, gdy ktoś tam jest, i wiadomość po przybyciu.",
|
||||
"on_complete_action_title": "Po zakończeniu: wywołaj akcję HA (opcjonalnie)",
|
||||
"on_complete_action_desc": "Wywołuje usługę HA po zakończeniu zadania — np. zresetuj licznik urządzenia.",
|
||||
"on_complete_action_target": "Encja docelowa",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Używa części: {name}",
|
||||
"label_filter": "Etykieta",
|
||||
"all_labels": "Wszystkie etykiety",
|
||||
"filter_place": "Miejsce",
|
||||
"filter_place_all": "Wszystkie miejsca",
|
||||
"settings_notify_scope": "Powiadamiaj tylko dla widoku",
|
||||
"settings_notify_scope_all": "Wszystkie zadania",
|
||||
"settings_notify_scope_hint": "Przypomnienia wysyłają tylko zadania pasujące do filtrów etykiety/użytkownika wybranego widoku. Status, sortowanie i grupowanie widoku są tu ignorowane.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Ainda não há objetos.",
|
||||
"action_error": "A ação falhou. Tente novamente.",
|
||||
"area_id_optional": "Área (opcional)",
|
||||
"place_optional": "Local (zona, opcional)",
|
||||
"remind_on_site": "Lembrar só quando alguém estiver lá",
|
||||
"remind_on_site_hint": "Os lembretes esperam até alguém estar nesta zona. Quem chega lá recebe uma mensagem com o que está pendente.",
|
||||
"place_missing": "Zona não encontrada: lembretes como em casa",
|
||||
"place_home": "Casa",
|
||||
"installation_date_optional": "Data de instalação (opcional)",
|
||||
"warranty_expiry_optional": "Fim da garantia (opcional)",
|
||||
"warranty": "Garantia",
|
||||
@@ -813,6 +818,8 @@
|
||||
"owner_label": "Proprietário",
|
||||
"feat_completion_actions": "Ações de conclusão",
|
||||
"feat_completion_actions_desc": "Ação do HA por tarefa ao concluir + QR de conclusão rápida com valores predefinidos.",
|
||||
"feat_places": "Locais",
|
||||
"feat_places_desc": "Objetos mantidos em outro local (uma zona do Home Assistant): lembretes só quando alguém está lá e uma mensagem na chegada.",
|
||||
"on_complete_action_title": "Ao concluir: acionar ação do HA (opcional)",
|
||||
"on_complete_action_desc": "Chama um serviço do HA quando a tarefa é concluída — ex.: zerar um contador no dispositivo.",
|
||||
"on_complete_action_target": "Entidade alvo",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Usa a peça: {name}",
|
||||
"label_filter": "Etiqueta",
|
||||
"all_labels": "Todas as etiquetas",
|
||||
"filter_place": "Local",
|
||||
"filter_place_all": "Todos os locais",
|
||||
"settings_notify_scope": "Notificar apenas para a visualização",
|
||||
"settings_notify_scope_all": "Todas as tarefas",
|
||||
"settings_notify_scope_hint": "Apenas tarefas que correspondem aos filtros de etiqueta/usuário da visualização salva selecionada enviam lembretes. Status, ordenação e agrupamento da visualização são ignorados aqui.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Ainda sem objetos.",
|
||||
"action_error": "Ação falhada. Tente novamente.",
|
||||
"area_id_optional": "Área (opcional)",
|
||||
"place_optional": "Local (zona, opcional)",
|
||||
"remind_on_site": "Lembrar só quando alguém lá estiver",
|
||||
"remind_on_site_hint": "Os lembretes esperam até alguém estar nesta zona. Quem lá chega recebe uma mensagem com o que está pendente.",
|
||||
"place_missing": "Zona não encontrada: lembretes como em casa",
|
||||
"place_home": "Casa",
|
||||
"installation_date_optional": "Data de instalação (opcional)",
|
||||
"warranty_expiry_optional": "Validade da garantia (opcional)",
|
||||
"warranty": "Garantia",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Proprietário",
|
||||
"feat_completion_actions": "Ações de conclusão",
|
||||
"feat_completion_actions_desc": "Ação HA por tarefa ao concluir + QR de conclusão rápida com valores predefinidos.",
|
||||
"feat_places": "Locais",
|
||||
"feat_places_desc": "Objetos mantidos noutro local (uma zona do Home Assistant): lembretes só quando alguém lá está e uma mensagem à chegada.",
|
||||
"on_complete_action_title": "Na conclusão: acionar ação HA (opcional)",
|
||||
"on_complete_action_desc": "Chama um serviço HA quando a tarefa é concluída — p. ex. reiniciar um contador no dispositivo.",
|
||||
"on_complete_action_target": "Entidade alvo",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Usa a peça: {name}",
|
||||
"label_filter": "Etiqueta",
|
||||
"all_labels": "Todas as etiquetas",
|
||||
"filter_place": "Local",
|
||||
"filter_place_all": "Todos os locais",
|
||||
"settings_notify_scope": "Notificar apenas para a vista",
|
||||
"settings_notify_scope_all": "Todas as tarefas",
|
||||
"settings_notify_scope_hint": "Apenas as tarefas que correspondem aos filtros de etiqueta/utilizador da vista escolhida enviam lembretes. O estado, a ordenação e o agrupamento da vista são ignorados aqui.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Пока нет объектов.",
|
||||
"action_error": "Не удалось выполнить действие. Попробуйте ещё раз.",
|
||||
"area_id_optional": "Зона (опционально)",
|
||||
"place_optional": "Место (зона, необязательно)",
|
||||
"remind_on_site": "Напоминать, только когда там кто-то есть",
|
||||
"remind_on_site_hint": "Напоминания ждут, пока кто-нибудь окажется в этой зоне. Кто туда приезжает, получает сообщение о том, что нужно сделать.",
|
||||
"place_missing": "Зона не найдена: напоминания как дома",
|
||||
"place_home": "Дом",
|
||||
"installation_date_optional": "Дата установки (опционально)",
|
||||
"warranty_expiry_optional": "Окончание гарантии (опционально)",
|
||||
"warranty": "Гарантия",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Владелец",
|
||||
"feat_completion_actions": "Действия при завершении",
|
||||
"feat_completion_actions_desc": "HA-действие по задаче при завершении + QR быстрого завершения с предустановленными значениями.",
|
||||
"feat_places": "Места",
|
||||
"feat_places_desc": "Объекты в другом месте (зона Home Assistant): напоминания только когда там кто-то есть, и сообщение по прибытии.",
|
||||
"on_complete_action_title": "При завершении: запустить HA-действие (опционально)",
|
||||
"on_complete_action_desc": "Вызывает HA-сервис при завершении задачи — напр., сбросить счётчик на устройстве.",
|
||||
"on_complete_action_target": "Целевая сущность",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Использует деталь: {name}",
|
||||
"label_filter": "Метка",
|
||||
"all_labels": "Все метки",
|
||||
"filter_place": "Место",
|
||||
"filter_place_all": "Все места",
|
||||
"settings_notify_scope": "Уведомлять только для представления",
|
||||
"settings_notify_scope_all": "Все задачи",
|
||||
"settings_notify_scope_hint": "Напоминания отправляют только задачи, совпадающие с фильтрами метки/пользователя выбранного представления. Статус, сортировка и группировка представления здесь игнорируются.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Inga objekt än.",
|
||||
"action_error": "Åtgärden misslyckades. Försök igen.",
|
||||
"area_id_optional": "Område (valfritt)",
|
||||
"place_optional": "Plats (zon, valfritt)",
|
||||
"remind_on_site": "Påminn bara när någon är där",
|
||||
"remind_on_site_hint": "Påminnelser väntar tills någon är i den här zonen. Den som kommer dit får ett meddelande med det som ska göras.",
|
||||
"place_missing": "Zonen hittades inte: påminnelser som hemma",
|
||||
"place_home": "Hemma",
|
||||
"installation_date_optional": "Installationsdatum (valfritt)",
|
||||
"warranty_expiry_optional": "Garantins utgång (valfritt)",
|
||||
"warranty": "Garanti",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Ägare",
|
||||
"feat_completion_actions": "Slutförande-åtgärder",
|
||||
"feat_completion_actions_desc": "HA-åtgärd per uppgift vid slutförande + snabb-slutför-QR med förinställda värden.",
|
||||
"feat_places": "Platser",
|
||||
"feat_places_desc": "Objekt som sköts på en annan plats (en Home Assistant-zon): påminnelser bara när någon är där, och ett meddelande vid ankomst.",
|
||||
"on_complete_action_title": "Vid slutförande: utlös HA-åtgärd (valfritt)",
|
||||
"on_complete_action_desc": "Anropar en HA-tjänst när uppgiften slutförs — t.ex. återställ en räknare på enheten.",
|
||||
"on_complete_action_target": "Målentitet",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Använder reservdel: {name}",
|
||||
"label_filter": "Etikett",
|
||||
"all_labels": "Alla etiketter",
|
||||
"filter_place": "Plats",
|
||||
"filter_place_all": "Alla platser",
|
||||
"settings_notify_scope": "Avisera endast för vy",
|
||||
"settings_notify_scope_all": "Alla uppgifter",
|
||||
"settings_notify_scope_hint": "Endast uppgifter som matchar den valda vyns etikett-/användarfilter skickar påminnelser. Vyns status, sortering och gruppering ignoreras här.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "Henüz nesne yok.",
|
||||
"action_error": "Eylem başarısız oldu. Lütfen tekrar deneyin.",
|
||||
"area_id_optional": "Alan (isteğe bağlı)",
|
||||
"place_optional": "Yer (bölge, isteğe bağlı)",
|
||||
"remind_on_site": "Yalnızca biri oradayken hatırlat",
|
||||
"remind_on_site_hint": "Hatırlatmalar biri bu bölgede olana kadar bekler. Oraya varan kişi yapılacakların listesini içeren bir mesaj alır.",
|
||||
"place_missing": "Bölge bulunamadı: hatırlatmalar evdeki gibi",
|
||||
"place_home": "Ev",
|
||||
"installation_date_optional": "Kurulum tarihi (isteğe bağlı)",
|
||||
"warranty_expiry_optional": "Garanti bitişi (isteğe bağlı)",
|
||||
"warranty": "Garanti",
|
||||
@@ -813,6 +818,8 @@
|
||||
"owner_label": "Sahip",
|
||||
"feat_completion_actions": "Tamamlama eylemleri",
|
||||
"feat_completion_actions_desc": "Görev başına tamamlamada HA eylemi + önceden ayarlı değerlerle hızlı tamamlama QR'ı.",
|
||||
"feat_places": "Yerler",
|
||||
"feat_places_desc": "Başka bir yerde (Home Assistant bölgesi) bakımı yapılan nesneler: hatırlatmalar yalnızca biri oradayken, varışta bir mesaj.",
|
||||
"on_complete_action_title": "Tamamlandığında: HA eylemi tetikle (isteğe bağlı)",
|
||||
"on_complete_action_desc": "Görev tamamlandığında bir HA servisi çağırır — örn. cihazdaki bir sayacı sıfırlar.",
|
||||
"on_complete_action_target": "Hedef varlık",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Kullanılan parça: {name}",
|
||||
"label_filter": "Etiket",
|
||||
"all_labels": "Tüm etiketler",
|
||||
"filter_place": "Yer",
|
||||
"filter_place_all": "Tüm yerler",
|
||||
"settings_notify_scope": "Yalnızca şu görünüm için bildir",
|
||||
"settings_notify_scope_all": "Tüm görevler",
|
||||
"settings_notify_scope_hint": "Yalnızca seçili kayıtlı görünümün etiket/kullanıcı filtreleriyle eşleşen görevler hatırlatma gönderir. Görünümün durum, sıralama ve gruplaması burada dikkate alınmaz.",
|
||||
|
||||
@@ -545,6 +545,11 @@
|
||||
"no_objects": "Поки немає об'єктів.",
|
||||
"action_error": "Дія не вдалась. Спробуйте ще раз.",
|
||||
"area_id_optional": "Зона (необов'язково)",
|
||||
"place_optional": "Місце (зона, необов'язково)",
|
||||
"remind_on_site": "Нагадувати, лише коли там хтось є",
|
||||
"remind_on_site_hint": "Нагадування чекають, поки хтось буде в цій зоні. Хто туди прибуває, отримує повідомлення про те, що потрібно зробити.",
|
||||
"place_missing": "Зону не знайдено: нагадування як удома",
|
||||
"place_home": "Дім",
|
||||
"installation_date_optional": "Дата встановлення (необов'язково)",
|
||||
"warranty_expiry_optional": "Закінчення гарантії (необов'язково)",
|
||||
"warranty": "Гарантія",
|
||||
@@ -781,6 +786,8 @@
|
||||
"owner_label": "Власник",
|
||||
"feat_completion_actions": "Дії при завершенні",
|
||||
"feat_completion_actions_desc": "Дія HA по завданню при завершенні + QR швидкого завершення з попередньо встановленими значеннями.",
|
||||
"feat_places": "Місця",
|
||||
"feat_places_desc": "Об'єкти в іншому місці (зона Home Assistant): нагадування лише тоді, коли там хтось є, і повідомлення після прибуття.",
|
||||
"on_complete_action_title": "При завершенні: викликати HA-дію (опційно)",
|
||||
"on_complete_action_desc": "Викликає HA-сервіс, коли завдання завершено — напр., скинути лічильник на пристрої.",
|
||||
"on_complete_action_target": "Цільова сутність",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "Використовує деталь: {name}",
|
||||
"label_filter": "Мітка",
|
||||
"all_labels": "Усі мітки",
|
||||
"filter_place": "Місце",
|
||||
"filter_place_all": "Усі місця",
|
||||
"settings_notify_scope": "Сповіщати лише для подання",
|
||||
"settings_notify_scope_all": "Усі завдання",
|
||||
"settings_notify_scope_hint": "Нагадування надсилають лише завдання, що відповідають фільтрам мітки/користувача обраного подання. Статус, сортування та групування подання тут ігноруються.",
|
||||
|
||||
@@ -547,6 +547,11 @@
|
||||
"no_objects": "暂无维护项。",
|
||||
"action_error": "操作失败。请重试。",
|
||||
"area_id_optional": "区域 (可选)",
|
||||
"place_optional": "地点(区域,可选)",
|
||||
"remind_on_site": "仅当有人在场时提醒",
|
||||
"remind_on_site_hint": "提醒会等到有人在此区域时再发送。到达那里的人会收到一条列出待办事项的消息。",
|
||||
"place_missing": "未找到区域:按在家处理提醒",
|
||||
"place_home": "家",
|
||||
"installation_date_optional": "安装日期 (可选)",
|
||||
"warranty_expiry_optional": "保修到期 (可选)",
|
||||
"warranty": "保修",
|
||||
@@ -806,6 +811,8 @@
|
||||
"owner_label": "所有者",
|
||||
"feat_completion_actions": "完成动作",
|
||||
"feat_completion_actions_desc": "为每个任务配置完成时的 HA 动作 + 预设值的快速完成二维码。",
|
||||
"feat_places": "地点",
|
||||
"feat_places_desc": "在其他地点(Home Assistant 区域)维护的对象:仅当有人在场时提醒,并在到达时发送消息。",
|
||||
"on_complete_action_title": "完成时:触发 HA 动作(可选)",
|
||||
"on_complete_action_desc": "任务完成时调用 HA 服务 — 例如重置设备上的物理计数器。",
|
||||
"on_complete_action_target": "目标实体",
|
||||
@@ -1125,6 +1132,8 @@
|
||||
"adopt_problem_part": "使用配件:{name}",
|
||||
"label_filter": "标签",
|
||||
"all_labels": "所有标签",
|
||||
"filter_place": "地点",
|
||||
"filter_place_all": "所有地点",
|
||||
"settings_notify_scope": "仅为该视图发送通知",
|
||||
"settings_notify_scope_all": "所有任务",
|
||||
"settings_notify_scope_hint": "只有匹配所选视图的标签/用户筛选的任务才会发送提醒。视图的状态、排序和分组在此处被忽略。",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*! maintenance_supporter frontend 2.98.0 */
|
||||
var fr=Object.defineProperty;var _a=Object.getOwnPropertyDescriptor;var y=(s,r,e)=>()=>{if(e)throw e[0];try{return s&&(r=s(s=0)),r}catch(t){throw e=[t],t}};var ma=(s,r)=>{for(var e in r)fr(s,e,{get:r[e],enumerable:!0})};var c=(s,r,e,t)=>{for(var i=t>1?void 0:t?_a(r,e):r,a=s.length-1,o;a>=0;a--)(o=s[a])&&(i=(t?o(r,e,i):o(i))||i);return t&&i&&fr(r,e,i),i};var Rt,Pt,mi,vr,ct,yr,E,br,gi,fi=y(()=>{Rt=globalThis,Pt=Rt.ShadowRoot&&(Rt.ShadyCSS===void 0||Rt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,mi=Symbol(),vr=new WeakMap,ct=class{constructor(r,e,t){if(this._$cssResult$=!0,t!==mi)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=r,this.t=e}get styleSheet(){let r=this.o,e=this.t;if(Pt&&r===void 0){let t=e!==void 0&&e.length===1;t&&(r=vr.get(e)),r===void 0&&((this.o=r=new CSSStyleSheet).replaceSync(this.cssText),t&&vr.set(e,r))}return r}toString(){return this.cssText}},yr=s=>new ct(typeof s=="string"?s:s+"",void 0,mi),E=(s,...r)=>{let e=s.length===1?s[0]:r.reduce((t,i,a)=>t+(o=>{if(o._$cssResult$===!0)return o.cssText;if(typeof o=="number")return o;throw Error("Value passed to 'css' function must be a 'css' function result: "+o+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+s[a+1],s[0]);return new ct(e,s,mi)},br=(s,r)=>{if(Pt)s.adoptedStyleSheets=r.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let e of r){let t=document.createElement("style"),i=Rt.litNonce;i!==void 0&&t.setAttribute("nonce",i),t.textContent=e.cssText,s.appendChild(t)}},gi=Pt?s=>s:s=>s instanceof CSSStyleSheet?(r=>{let e="";for(let t of r.cssRules)e+=t.cssText;return yr(e)})(s):s});var ga,fa,va,ya,ba,xa,Lt,xr,wa,ka,dt,pt,Nt,wr,ce,ut=y(()=>{fi();fi();({is:ga,defineProperty:fa,getOwnPropertyDescriptor:va,getOwnPropertyNames:ya,getOwnPropertySymbols:ba,getPrototypeOf:xa}=Object),Lt=globalThis,xr=Lt.trustedTypes,wa=xr?xr.emptyScript:"",ka=Lt.reactiveElementPolyfillSupport,dt=(s,r)=>s,pt={toAttribute(s,r){switch(r){case Boolean:s=s?wa:null;break;case Object:case Array:s=s==null?s:JSON.stringify(s)}return s},fromAttribute(s,r){let e=s;switch(r){case Boolean:e=s!==null;break;case Number:e=s===null?null:Number(s);break;case Object:case Array:try{e=JSON.parse(s)}catch{e=null}}return e}},Nt=(s,r)=>!ga(s,r),wr={attribute:!0,type:String,converter:pt,reflect:!1,useDefault:!1,hasChanged:Nt};Symbol.metadata??=Symbol("metadata"),Lt.litPropertyMetadata??=new WeakMap;ce=class extends HTMLElement{static addInitializer(r){this._$Ei(),(this.l??=[]).push(r)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(r,e=wr){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(r)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(r,e),!e.noAccessor){let t=Symbol(),i=this.getPropertyDescriptor(r,t,e);i!==void 0&&fa(this.prototype,r,i)}}static getPropertyDescriptor(r,e,t){let{get:i,set:a}=va(this.prototype,r)??{get(){return this[e]},set(o){this[e]=o}};return{get:i,set(o){let d=i?.call(this);a?.call(this,o),this.requestUpdate(r,d,t)},configurable:!0,enumerable:!0}}static getPropertyOptions(r){return this.elementProperties.get(r)??wr}static _$Ei(){if(this.hasOwnProperty(dt("elementProperties")))return;let r=xa(this);r.finalize(),r.l!==void 0&&(this.l=[...r.l]),this.elementProperties=new Map(r.elementProperties)}static finalize(){if(this.hasOwnProperty(dt("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(dt("properties"))){let e=this.properties,t=[...ya(e),...ba(e)];for(let i of t)this.createProperty(i,e[i])}let r=this[Symbol.metadata];if(r!==null){let e=litPropertyMetadata.get(r);if(e!==void 0)for(let[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let i=this._$Eu(e,t);i!==void 0&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(r){let e=[];if(Array.isArray(r)){let t=new Set(r.flat(1/0).reverse());for(let i of t)e.unshift(gi(i))}else r!==void 0&&e.push(gi(r));return e}static _$Eu(r,e){let t=e.attribute;return t===!1?void 0:typeof t=="string"?t:typeof r=="string"?r.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(r=>this.enableUpdating=r),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(r=>r(this))}addController(r){(this._$EO??=new Set).add(r),this.renderRoot!==void 0&&this.isConnected&&r.hostConnected?.()}removeController(r){this._$EO?.delete(r)}_$E_(){let r=new Map,e=this.constructor.elementProperties;for(let t of e.keys())this.hasOwnProperty(t)&&(r.set(t,this[t]),delete this[t]);r.size>0&&(this._$Ep=r)}createRenderRoot(){let r=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return br(r,this.constructor.elementStyles),r}connectedCallback(){this.renderRoot??=tLine truncated
|
||||
\f\r]`,ht=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Er=/-->/g,Sr=/>/g,Se=RegExp(`>|${vi}(?:([^\\s"'>=/]+)(${vi}*=${vi}*(?:[^
|
||||
\f\r"'\`<>=]|("|')|))|$)`,"g"),Tr=/'/g,Ar=/"/g,Pr=/^(?:script|style|textarea|title)$/i,Si=s=>(r,...e)=>({_$litType$:s,strings:r,values:e}),l=Si(1),We=Si(2),bl=Si(3),Ce=Symbol.for("lit-noChange"),h=Symbol.for("lit-nothing"),Cr=new WeakMap,Te=Ae.createTreeWalker(Ae,129);Sa=(s,r)=>{let e=s.length-1,t=[],i,a=r===2?"<svg>":r===3?"<math>":"",o=ht;for(let d=0;d<e;d++){let p=s[d],_,m,f=-1,x=0;for(;x<p.length&&(o.lastIndex=x,m=o.exec(p),m!==null);)x=o.lastIndex,o===ht?m[1]==="!--"?o=Er:m[1]!==void 0?o=Sr:m[2]!==void 0?(Pr.test(m[2])&&(i=RegExp("</"+m[2],"g")),o=Se):m[3]!==void 0&&(o=Se):o===Se?m[0]===">"?(o=i??ht,f=-1):m[1]===void 0?f=-2:(f=o.lastIndex-m[2].length,_=m[1],o=m[3]===void 0?Se:m[3]==='"'?Ar:Tr):o===Ar||o===Tr?o=Se:o===Er||o===Sr?o=ht:(o=Se,i=void 0);let w=o===Se&&s[d+1].startsWith("/>")?" ":"";a+=o===ht?p+$a:f>=0?(t.push(_),p.slice(0,f)+Ir+p.slice(f)+me+w):p+me+(f===-2?d:w)}return[Lr(s,a+(s[e]||"<?>")+(r===2?"</svg>":r===3?"</math>":"")),t]},gt=class s{constructor({strings:r,_$litType$:e},t){let i;this.parts=[];let a=0,o=0,d=r.length-1,p=this.parts,[_,m]=Sa(r,e);if(this.el=s.createElement(_,t),Te.currentNode=this.el.content,e===2||e===3){let f=this.el.content.firstChild;f.replaceWith(...f.childNodes)}for(;(i=Te.nextNode())!==null&&p.length<d;){if(i.nodeType===1){if(i.hasAttributes())for(let f of i.getAttributeNames())if(f.endsWith(Ir)){let x=m[o++],w=i.getAttribute(f).split(me),k=/([.?@])?(.*)/.exec(x);p.push({type:1,index:a,name:k[2],strings:w,ctor:k[1]==="."?bi:k[1]==="?"?xi:k[1]==="@"?wi:Be}),i.removeAttribute(f)}else f.startsWith(me)&&(p.push({type:6,index:a}),i.removeAttribute(f));if(Pr.test(i.tagName)){let f=i.textContent.split(me),x=f.length-1;if(x>0){i.textContent=qt?qt.emptyScript:"";for(let w=0;w<x;w++)i.append(f[w],_t()),Te.nextNode(),p.push({type:2,index:++a});i.append(f[x],_t())}}}else if(i.nodeType===8)if(i.data===Rr)p.push({type:2,index:a});else{let f=-1;for(;(f=i.data.indexOf(me,f+1))!==-1;)p.push({type:7,index:a}),f+=me.length-1}a++}}static createElement(r,e){let t=Ae.createElement("template");return t.innerHTML=r,t}};yi=class{constructor(r,e){this._$AV=[],this._$AN=void 0,this._$AD=r,this._$AM=e}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(r){let{el:{content:e},parts:t}=this._$AD,i=(r?.creationScope??Ae).importNode(e,!0);Te.currentNode=i;let a=Te.nextNode(),o=0,d=0,p=t[0];for(;p!==void 0;){if(o===p.index){let _;p.type===2?_=new ft(a,a.nextSibling,this,r):p.type===1?_=new p.ctor(a,p.name,p.strings,this,r):p.type===6&&(_=new ki(a,this,r)),this._$AV.push(_),p=t[++d]}o!==p?.index&&(a=Te.nextNode(),o++)}return Te.currentNode=Ae,i}p(r){let e=0;for(let t of this._$AV)t!==void 0&&(t.strings!==void 0?(t._$AI(r,t,e),e+=t.strings.length-2):t._$AI(r[e])),e++}},ft=class s{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(r,e,t,i){this.type=2,this._$AH=h,this._$AN=void 0,this._$AA=r,this._$AB=e,this._$AM=t,this.options=i,this._$Cv=i?.isConnected??!0}get parentNode(){let r=this._$AA.parentNode,e=this._$AM;return e!==void 0&&r?.nodeType===11&&(r=e.parentNode),r}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(r,e=this){r=Ue(this,r,e),mt(r)?r===h||r==null||r===""?(this._$AH!==h&&this._$AR(),this._$AH=h):r!==this._$AH&&r!==Ce&&this._(r):r._$litType$!==void 0?this.$(r):r.nodeType!==void 0?this.T(r):Ea(r)?this.k(r):this._(r)}O(r){return this._$AA.parentNode.insertBefore(r,this._$AB)}T(r){this._$AH!==r&&(this._$AR(),this._$AH=this.O(r))}_(r){this._$AH!==h&&mt(this._$AH)?this._$AA.nextSibling.data=r:this.T(Ae.createTextNode(r)),this._$AH=r}$(r){let{values:e,_$litType$:t}=r,i=typeof t=="number"?this._$AC(r):(t.el===void 0&&(t.el=gt.createElement(Lr(t.h,t.h[0]),this.options)),t);if(this._$AH?._$AD===i)this._$AH.p(e);else{let a=new yi(i,this),o=a.u(this.options);a.p(e),this.T(o),this._$AH=a}}_$AC(r){let e=Cr.get(r.strings);return e===void 0&&Cr.set(r.strings,e=new gt(r)),e}k(r){Ei(this._$AH)||(this._$AH=[],this._$AR());let e=this._$AH,t,i=0;for(let a of r)i===e.length?e.push(t=new s(this.O(_t()),this.O(_t()),this,this.options)):t=e[i],t._$AI(a),i++;i<e.length&&(this._$AR(t&&t._$AB.nextSibling,i),e.length=i)}_$AR(r=this._$AA.nextSibling,e){for(this._$AP?.(!1,!0,e);r!==this._$AB;){let t=kr(r).nextSibling;kr(r).remove(),r=t}}setConnected(r){this._$AM===void 0&&(this._$Cv=r,this._$AP?.(r))}},Be=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(r,e,t,i,a){this.type=1,this._$AH=h,this._$AN=void 0,this.element=r,this.name=e,this._$AM=i,this.options=a,t.length>2||t[0]!==""||t[1]!==""?(this._$AH=Array(t.length-1).fill(new String),this.strings=t):this._$AH=h}_$AI(r,e=this,t,i){let a=this.strings,o=!1;if(a===void 0)r=Ue(this,r,e,0),o=!mt(r)||r!==this._$AH&&r!==Ce,o&&(this._$AH=r);else{let d=r,p,_;for(r=a[0],p=0;p<a.length-1;p++)_=Ue(this,d[t+p],e,p),_===Ce&&(_=this._$AH[p]),o||=!mt(_)||_!==this._$AH[p],_===h?r=h:r!==h&&(r+=(_??"")+a[p+1]),this._$AH[p]=_}o&&!i&&this.j(r)}j(r){r===h?this.element.removeAttribute(this.naLine truncated
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
var vr=Object.defineProperty;var va=Object.getOwnPropertyDescriptor;var y=(s,r,e)=>()=>{if(e)throw e[0];try{return s&&(r=s(s=0)),r}catch(t){throw e=[t],t}};var ya=(s,r)=>{for(var e in r)vr(s,e,{get:r[e],enumerable:!0})};var c=(s,r,e,t)=>{for(var i=t>1?void 0:t?va(r,e):r,a=s.length-1,o;a>=0;a--)(o=s[a])&&(i=(t?o(r,e,i):o(i))||i);return t&&i&&vr(r,e,i),i};var Pt,Lt,gi,yr,ct,br,E,xr,fi,vi=y(()=>{Pt=globalThis,Lt=Pt.ShadowRoot&&(Pt.ShadyCSS===void 0||Pt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,gi=Symbol(),yr=new WeakMap,ct=class{constructor(r,e,t){if(this._$cssResult$=!0,t!==gi)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=r,this.t=e}get styleSheet(){let r=this.o,e=this.t;if(Lt&&r===void 0){let t=e!==void 0&&e.length===1;t&&(r=yr.get(e)),r===void 0&&((this.o=r=new CSSStyleSheet).replaceSync(this.cssText),t&&yr.set(e,r))}return r}toString(){return this.cssText}},br=s=>new ct(typeof s=="string"?s:s+"",void 0,gi),E=(s,...r)=>{let e=s.length===1?s[0]:r.reduce((t,i,a)=>t+(o=>{if(o._$cssResult$===!0)return o.cssText;if(typeof o=="number")return o;throw Error("Value passed to 'css' function must be a 'css' function result: "+o+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+s[a+1],s[0]);return new ct(e,s,gi)},xr=(s,r)=>{if(Lt)s.adoptedStyleSheets=r.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let e of r){let t=document.createElement("style"),i=Pt.litNonce;i!==void 0&&t.setAttribute("nonce",i),t.textContent=e.cssText,s.appendChild(t)}},fi=Lt?s=>s:s=>s instanceof CSSStyleSheet?(r=>{let e="";for(let t of r.cssRules)e+=t.cssText;return br(e)})(s):s});var ba,xa,wa,ka,$a,Ea,Nt,wr,Sa,Ta,dt,pt,qt,kr,ce,ut=y(()=>{vi();vi();({is:ba,defineProperty:xa,getOwnPropertyDescriptor:wa,getOwnPropertyNames:ka,getOwnPropertySymbols:$a,getPrototypeOf:Ea}=Object),Nt=globalThis,wr=Nt.trustedTypes,Sa=wr?wr.emptyScript:"",Ta=Nt.reactiveElementPolyfillSupport,dt=(s,r)=>s,pt={toAttribute(s,r){switch(r){case Boolean:s=s?Sa:null;break;case Object:case Array:s=s==null?s:JSON.stringify(s)}return s},fromAttribute(s,r){let e=s;switch(r){case Boolean:e=s!==null;break;case Number:e=s===null?null:Number(s);break;case Object:case Array:try{e=JSON.parse(s)}catch{e=null}}return e}},qt=(s,r)=>!ba(s,r),kr={attribute:!0,type:String,converter:pt,reflect:!1,useDefault:!1,hasChanged:qt};Symbol.metadata??=Symbol("metadata"),Nt.litPropertyMetadata??=new WeakMap;ce=class extends HTMLElement{static addInitializer(r){this._$Ei(),(this.l??=[]).push(r)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(r,e=kr){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(r)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(r,e),!e.noAccessor){let t=Symbol(),i=this.getPropertyDescriptor(r,t,e);i!==void 0&&xa(this.prototype,r,i)}}static getPropertyDescriptor(r,e,t){let{get:i,set:a}=wa(this.prototype,r)??{get(){return this[e]},set(o){this[e]=o}};return{get:i,set(o){let d=i?.call(this);a?.call(this,o),this.requestUpdate(r,d,t)},configurable:!0,enumerable:!0}}static getPropertyOptions(r){return this.elementProperties.get(r)??kr}static _$Ei(){if(this.hasOwnProperty(dt("elementProperties")))return;let r=Ea(this);r.finalize(),r.l!==void 0&&(this.l=[...r.l]),this.elementProperties=new Map(r.elementProperties)}static finalize(){if(this.hasOwnProperty(dt("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(dt("properties"))){let e=this.properties,t=[...ka(e),...$a(e)];for(let i of t)this.createProperty(i,e[i])}let r=this[Symbol.metadata];if(r!==null){let e=litPropertyMetadata.get(r);if(e!==void 0)for(let[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let i=this._$Eu(e,t);i!==void 0&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(r){let e=[];if(Array.isArray(r)){let t=new Set(r.flat(1/0).reverse());for(let i of t)e.unshift(fi(i))}else r!==void 0&&e.push(fi(r));return e}static _$Eu(r,e){let t=e.attribute;return t===!1?void 0:typeof t=="string"?t:typeof r=="string"?r.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(r=>this.enableUpdating=r),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(r=>r(this))}addController(r){(this._$EO??=new Set).add(r),this.renderRoot!==void 0&&this.isConnected&&r.hostConnected?.()}removeController(r){this._$EO?.delete(r)}_$E_(){let r=new Map,e=this.constructor.elementProperties;for(let t of e.keys())this.hasOwnProperty(t)&&(r.set(t,this[t]),delete this[t]);r.size>0&&(this._$Ep=r)}createRenderRoot(){let r=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return xr(r,this.constructor.elementStyles),r}connectedCallback(){this.renderRoot??=tLine truncated
|
||||
\f\r]`,ht=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Sr=/-->/g,Tr=/>/g,Se=RegExp(`>|${yi}(?:([^\\s"'>=/]+)(${yi}*=${yi}*(?:[^
|
||||
\f\r"'\`<>=]|("|')|))|$)`,"g"),Ar=/'/g,Cr=/"/g,Lr=/^(?:script|style|textarea|title)$/i,Ti=s=>(r,...e)=>({_$litType$:s,strings:r,values:e}),l=Ti(1),We=Ti(2),$l=Ti(3),Ce=Symbol.for("lit-noChange"),h=Symbol.for("lit-nothing"),Ir=new WeakMap,Te=Ae.createTreeWalker(Ae,129);Ia=(s,r)=>{let e=s.length-1,t=[],i,a=r===2?"<svg>":r===3?"<math>":"",o=ht;for(let d=0;d<e;d++){let p=s[d],_,m,f=-1,x=0;for(;x<p.length&&(o.lastIndex=x,m=o.exec(p),m!==null);)x=o.lastIndex,o===ht?m[1]==="!--"?o=Sr:m[1]!==void 0?o=Tr:m[2]!==void 0?(Lr.test(m[2])&&(i=RegExp("</"+m[2],"g")),o=Se):m[3]!==void 0&&(o=Se):o===Se?m[0]===">"?(o=i??ht,f=-1):m[1]===void 0?f=-2:(f=o.lastIndex-m[2].length,_=m[1],o=m[3]===void 0?Se:m[3]==='"'?Cr:Ar):o===Cr||o===Ar?o=Se:o===Sr||o===Tr?o=ht:(o=Se,i=void 0);let w=o===Se&&s[d+1].startsWith("/>")?" ":"";a+=o===ht?p+Aa:f>=0?(t.push(_),p.slice(0,f)+Rr+p.slice(f)+me+w):p+me+(f===-2?d:w)}return[Nr(s,a+(s[e]||"<?>")+(r===2?"</svg>":r===3?"</math>":"")),t]},gt=class s{constructor({strings:r,_$litType$:e},t){let i;this.parts=[];let a=0,o=0,d=r.length-1,p=this.parts,[_,m]=Ia(r,e);if(this.el=s.createElement(_,t),Te.currentNode=this.el.content,e===2||e===3){let f=this.el.content.firstChild;f.replaceWith(...f.childNodes)}for(;(i=Te.nextNode())!==null&&p.length<d;){if(i.nodeType===1){if(i.hasAttributes())for(let f of i.getAttributeNames())if(f.endsWith(Rr)){let x=m[o++],w=i.getAttribute(f).split(me),k=/([.?@])?(.*)/.exec(x);p.push({type:1,index:a,name:k[2],strings:w,ctor:k[1]==="."?xi:k[1]==="?"?wi:k[1]==="@"?ki:Be}),i.removeAttribute(f)}else f.startsWith(me)&&(p.push({type:6,index:a}),i.removeAttribute(f));if(Lr.test(i.tagName)){let f=i.textContent.split(me),x=f.length-1;if(x>0){i.textContent=Ht?Ht.emptyScript:"";for(let w=0;w<x;w++)i.append(f[w],_t()),Te.nextNode(),p.push({type:2,index:++a});i.append(f[x],_t())}}}else if(i.nodeType===8)if(i.data===Pr)p.push({type:2,index:a});else{let f=-1;for(;(f=i.data.indexOf(me,f+1))!==-1;)p.push({type:7,index:a}),f+=me.length-1}a++}}static createElement(r,e){let t=Ae.createElement("template");return t.innerHTML=r,t}};bi=class{constructor(r,e){this._$AV=[],this._$AN=void 0,this._$AD=r,this._$AM=e}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(r){let{el:{content:e},parts:t}=this._$AD,i=(r?.creationScope??Ae).importNode(e,!0);Te.currentNode=i;let a=Te.nextNode(),o=0,d=0,p=t[0];for(;p!==void 0;){if(o===p.index){let _;p.type===2?_=new ft(a,a.nextSibling,this,r):p.type===1?_=new p.ctor(a,p.name,p.strings,this,r):p.type===6&&(_=new $i(a,this,r)),this._$AV.push(_),p=t[++d]}o!==p?.index&&(a=Te.nextNode(),o++)}return Te.currentNode=Ae,i}p(r){let e=0;for(let t of this._$AV)t!==void 0&&(t.strings!==void 0?(t._$AI(r,t,e),e+=t.strings.length-2):t._$AI(r[e])),e++}},ft=class s{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(r,e,t,i){this.type=2,this._$AH=h,this._$AN=void 0,this._$AA=r,this._$AB=e,this._$AM=t,this.options=i,this._$Cv=i?.isConnected??!0}get parentNode(){let r=this._$AA.parentNode,e=this._$AM;return e!==void 0&&r?.nodeType===11&&(r=e.parentNode),r}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(r,e=this){r=Ue(this,r,e),mt(r)?r===h||r==null||r===""?(this._$AH!==h&&this._$AR(),this._$AH=h):r!==this._$AH&&r!==Ce&&this._(r):r._$litType$!==void 0?this.$(r):r.nodeType!==void 0?this.T(r):Ca(r)?this.k(r):this._(r)}O(r){return this._$AA.parentNode.insertBefore(r,this._$AB)}T(r){this._$AH!==r&&(this._$AR(),this._$AH=this.O(r))}_(r){this._$AH!==h&&mt(this._$AH)?this._$AA.nextSibling.data=r:this.T(Ae.createTextNode(r)),this._$AH=r}$(r){let{values:e,_$litType$:t}=r,i=typeof t=="number"?this._$AC(r):(t.el===void 0&&(t.el=gt.createElement(Nr(t.h,t.h[0]),this.options)),t);if(this._$AH?._$AD===i)this._$AH.p(e);else{let a=new bi(i,this),o=a.u(this.options);a.p(e),this.T(o),this._$AH=a}}_$AC(r){let e=Ir.get(r.strings);return e===void 0&&Ir.set(r.strings,e=new gt(r)),e}k(r){Si(this._$AH)||(this._$AH=[],this._$AR());let e=this._$AH,t,i=0;for(let a of r)i===e.length?e.push(t=new s(this.O(_t()),this.O(_t()),this,this.options)):t=e[i],t._$AI(a),i++;i<e.length&&(this._$AR(t&&t._$AB.nextSibling,i),e.length=i)}_$AR(r=this._$AA.nextSibling,e){for(this._$AP?.(!1,!0,e);r!==this._$AB;){let t=$r(r).nextSibling;$r(r).remove(),r=t}}setConnected(r){this._$AM===void 0&&(this._$Cv=r,this._$AP?.(r))}},Be=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(r,e,t,i,a){this.type=1,this._$AH=h,this._$AN=void 0,this.element=r,this.name=e,this._$AM=i,this.options=a,t.length>2||t[0]!==""||t[1]!==""?(this._$AH=Array(t.length-1).fill(new String),this.strings=t):this._$AH=h}_$AI(r,e=this,t,i){let a=this.strings,o=!1;if(a===void 0)r=Ue(this,r,e,0),o=!mt(r)||r!==this._$AH&&r!==Ce,o&&(this._$AH=r);else{let d=r,p,_;for(r=a[0],p=0;p<a.length-1;p++)_=Ue(this,d[t+p],e,p),_===Ce&&(_=this._$AH[p]),o||=!mt(_)||_!==this._$AH[p],_===h?r=h:r!==h&&(r+=(_??"")+a[p+1]),this._$AH[p]=_}o&&!i&&this.j(r)}j(r){r===h?this.element.removeAttribute(this.naLine 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",ws_err_not_found:"Not found \u2014 it may have been deleted meanwhile",ws_err_invalid_input:"Invalid input",ws_err_invalid_date:"Invalid date",ws_err_invalid_format:"Invalid format",ws_err_invalid_url:"Invalid link",ws_err_invalid_mirror_todo:"Invalid to-do list",ws_err_not_configured:"Not set up yet",ws_err_not_loaded:"Not loaded yet \u2014 try again in a moment",ws_err_limit_reached:"Limit reached",ws_err_too_many:"Too many entries",ws_err_too_large:"Too large",ws_err_not_archived:"Not archived",ws_err_invalid_target:"Invalid target",ws_err_create_failed:"Could not be created",ws_err_archived:"Archived \u2014 restore it first",ws_err_already_archived:"Already archived",ws_err_unavailable:"Currently unavailable",ws_err_unauthorized:"Not permitted for your user",ws_err_too_many_views:"Too many saved views",ws_err_storage_unavailable:"Storage is not available",ws_err_replace_failed:"Replacement failed",ws_err_not_paused:"Not paused",ws_err_not_available:"Nothing available",ws_err_no_url:"No link given",ws_err_no_phases:"This task has no phases",ws_err_invalid_view:"Invalid view",ws_err_invalid_user:"Unknown user",ws_err_invalid_range:"Invalid range",ws_err_invalid_parent:"Invalid parent object",ws_err_invalid_device:"Unknown device",ws_err_empty_csv:"The CSV file is empty",ws_err_empty:"Nothing to import",ws_err_duplicate_failed:"Could not duplicate",ws_err_already_paused:"Already paused",ws_err_invalid_cursor:"Invalid phase position",ws_err_invalid_entity_slug:"Invalid entity name",ws_err_invalid_trigger_config:"Invalid trigger configuration",ws_err_no_defaults:"No quick-complete defaults set",ws_err_self_link_device:"An object cannot be linked to itself",ws_err_too_early:"Too early \u2014 this task can only be completed closer to its due date",ws_err_invalid_search_template:"The search link must be an http(s) URL containing {q}",ws_err_invalid_icon:"The notification icon must be an mdi: name, e.g. mdi:air-filter",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_toggle:"Due at a specific time",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",notes_markdown_hint:"Markdown is supported \u2014 **bold**, lists, [links](\u2026)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",completed_at_optional:"Completed at (optional, empty = now)",completed_at_pick:"Set date & time",completed_at_future_error:"The completion date cannot be in the future.",ha_time_zone_hint:"Time in Home Assistant's time zone ({zone})",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",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.",task_mirror_todo:"Mirror into to-do lists",task_mirror_todo_hint:"The task appears in these lists while it is due; checking it off there completes it here.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",all_priorities:"All priorities",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",object_replaces:"Replaces",object_replaced_by:"Replaced by",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",all_parts:"All parts",all_areas:"All areas",areas_filter:"Filter areas\u2026",areas_filter_none:"No area matches the filter.",area_cost_year:"Cost {year}",area_object_count:"{n} o
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",ws_err_not_found:"Not found \u2014 it may have been deleted meanwhile",ws_err_invalid_input:"Invalid input",ws_err_invalid_date:"Invalid date",ws_err_invalid_format:"Invalid format",ws_err_invalid_url:"Invalid link",ws_err_invalid_mirror_todo:"Invalid to-do list",ws_err_not_configured:"Not set up yet",ws_err_not_loaded:"Not loaded yet \u2014 try again in a moment",ws_err_limit_reached:"Limit reached",ws_err_too_many:"Too many entries",ws_err_too_large:"Too large",ws_err_not_archived:"Not archived",ws_err_invalid_target:"Invalid target",ws_err_create_failed:"Could not be created",ws_err_archived:"Archived \u2014 restore it first",ws_err_already_archived:"Already archived",ws_err_unavailable:"Currently unavailable",ws_err_unauthorized:"Not permitted for your user",ws_err_too_many_views:"Too many saved views",ws_err_storage_unavailable:"Storage is not available",ws_err_replace_failed:"Replacement failed",ws_err_not_paused:"Not paused",ws_err_not_available:"Nothing available",ws_err_no_url:"No link given",ws_err_no_phases:"This task has no phases",ws_err_invalid_view:"Invalid view",ws_err_invalid_user:"Unknown user",ws_err_invalid_range:"Invalid range",ws_err_invalid_parent:"Invalid parent object",ws_err_invalid_device:"Unknown device",ws_err_empty_csv:"The CSV file is empty",ws_err_empty:"Nothing to import",ws_err_duplicate_failed:"Could not duplicate",ws_err_already_paused:"Already paused",ws_err_invalid_cursor:"Invalid phase position",ws_err_invalid_entity_slug:"Invalid entity name",ws_err_invalid_trigger_config:"Invalid trigger configuration",ws_err_no_defaults:"No quick-complete defaults set",ws_err_self_link_device:"An object cannot be linked to itself",ws_err_too_early:"Too early \u2014 this task can only be completed closer to its due date",ws_err_invalid_search_template:"The search link must be an http(s) URL containing {q}",ws_err_invalid_icon:"The notification icon must be an mdi: name, e.g. mdi:air-filter",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_toggle:"Due at a specific time",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",notes_markdown_hint:"Markdown is supported \u2014 **bold**, lists, [links](\u2026)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",completed_at_optional:"Completed at (optional, empty = now)",completed_at_pick:"Set date & time",completed_at_future_error:"The completion date cannot be in the future.",ha_time_zone_hint:"Time in Home Assistant's time zone ({zone})",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",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.",task_mirror_todo:"Mirror into to-do lists",task_mirror_todo_hint:"The task appears in these lists while it is due; checking it off there completes it here.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",all_priorities:"All priorities",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",object_replaces:"Replaces",object_replaced_by:"Replaced by",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",all_parts:"All parts",all_areas:"All areas",areas_filter:"Filter areas\u2026",areas_filter_none:"No area matches the filter.",area_cost_year:"Cost {year}",area_object_count:"{n} o
Line truncated
|
||||
.field { display: flex; flex-direction: column; gap: 4px; }
|
||||
.field-label { font-size: 12px; color: var(--secondary-text-color); }
|
||||
.field-input {
|
||||
@@ -14,7 +14,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
font-family: inherit; width: 100%; box-sizing: border-box;
|
||||
}
|
||||
.field-input:focus { outline: none; border-color: var(--primary-color); }
|
||||
`,Qa=E`
|
||||
`,eo=E`
|
||||
.person-chip { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; vertical-align: middle; }
|
||||
.person-avatar {
|
||||
width: 22px; height: 22px; border-radius: 50%; flex: none;
|
||||
@@ -24,7 +24,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.person-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
||||
`,Za=E`
|
||||
`,to=E`
|
||||
.ref-chip {
|
||||
display: inline-block; font-size: 11.5px; font-weight: 500; line-height: 1; padding: 3px 6px;
|
||||
border-radius: 6px; border: 1px solid var(--divider-color); color: var(--secondary-text-color);
|
||||
@@ -33,9 +33,9 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
}
|
||||
/* #189: "· Residual waste, Paper" after a calendar-driven task's name. */
|
||||
.event-titles { color: var(--secondary-text-color); font-weight: 400; }
|
||||
`,Zt=E`
|
||||
${Qa}
|
||||
${Za}
|
||||
`,Jt=E`
|
||||
${eo}
|
||||
${to}
|
||||
:host {
|
||||
--maint-ok-color: var(--success-color, #4caf50);
|
||||
--maint-due-soon-color: var(--warning-color, #ff9800);
|
||||
@@ -1270,7 +1270,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.stat-item .stat-label { font-size: 11px; white-space: normal; text-align: center; line-height: 1.2; hyphens: auto; overflow-wrap: anywhere; }
|
||||
.stat-value { font-size: 20px; }
|
||||
}
|
||||
`});function Jt(s){if(!s)return[];let r=[],e=s.photo_doc_id;typeof e=="string"&&r.push(e);let t=s.photo_doc_ids;Array.isArray(t)&&r.push(...t);let i=[];for(let a of r){if(typeof a!="string")continue;let o=a.trim();if(!(!o||i.includes(o))&&(i.push(o),i.length>=10))break}return i}var Xt=y(()=>{"use strict"});function As(){let s=new Uint8Array(4);return(globalThis.crypto??{getRandomValues:r=>r.map(()=>Math.floor(Math.random()*256))}).getRandomValues(s),Array.from(s,r=>r.toString(16).padStart(2,"0")).join("")}function be(s){let r=s?.reading_values;if(!Array.isArray(r))return[];let e=[],t=new Set;for(let i of r){if(!i||typeof i!="object")continue;let a=i;typeof a.id!="string"||!a.id||t.has(a.id)||typeof a.value!="number"||!Number.isFinite(a.value)||(t.add(a.id),e.push({id:a.id,name:typeof a.name=="string"?a.name:a.id,unit:typeof a.unit=="string"?a.unit:null,value:a.value}))}return e}function Cs(s){return s.filter(r=>r.type==="completed"&&(r.reading_value!=null||be(r).length>0)).sort((r,e)=>r.timestamp.localeCompare(e.timestamp))}function Is(s){return Cs(s??[]).map(r=>({timestamp:r.timestamp,values:be(r)})).filter(r=>r.values.length>0)}function Rs(s,r,e){let t;for(let i of s){if(e!==void 0){let o=Q(i.timestamp);if(!isNaN(o)&&o>=e)break}let a=i.values.find(o=>o.id===r);a&&(t=a)}return t}function Ps(s,r,e){let t=be(r).find(a=>a.id===e);if(!t)return null;let i=null;for(let a of Cs(s)){if(a.timestamp>=r.timestamp)break;let o=be(a).find(d=>d.id===e);o&&(i=o.value)}return i==null?null:t.value-i}function Ls(s){let r=new Set,e=new Set;for(let t of s){let i=(t.name||"").trim().toLowerCase();i&&(r.has(i)?e.add(t.id):r.add(i))}return e}function Ns(s){let r=[],e=new Set,t=new Set;for(let i of s){let a=(i.name||"").trim();if(!(!a||e.has(i.id)||t.has(a.toLowerCase()))&&(e.add(i.id),t.add(a.toLowerCase()),r.push({id:i.id,name:a,unit:(i.unit||"").trim()||null}),r.length>=Ui))break}return r}var Ui,Oe=y(()=>{"use strict";Z();Ui=20});function Bi(s,r,e,t){let i=e,a=p=>typeof p=="string"?p:null,o=p=>typeof p=="number"?p:null,d=a(i.timestamp)??"";return{entry_id:s,task_id:r,original_timestamp:d,type:a(i.type)||"completed",timestamp:d,notes:a(i.notes),cost:o(i.cost),duration:o(i.duration),completed_by:a(i.completed_by),used_parts:Array.isArray(i.used_parts)?i.used_parts:null,photo_doc_ids:Jt(i),reading_value:o(i.reading_value),reading_values:be(i),readings:t?.readings??[],task_type:t?.type??null,reading_unit:t?.reading_unit??null}}async function qs(s,r,e,t){let a=(await s.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:r})).tasks?.find(d=>d.id===e);if(!a)return null;let o=a.history?.find(d=>d.timestamp===t);return o||(o=(await s.connection.sendMessagePromise({type:"maintenance_supporter/task/history",entry_id:r,task_id:e})).history?.find(p=>p.timestamp===t)),o?Bi(r,e,o,a):null}var Wi=y(()=>{"use strict";Xt();Oe()});function xe(s,r=je){return s?s.is_admin?!0:r.operatorWriteEnabled&&r.operatorIds.includes(s.id):!1}var je,xt=y(()=>{"use strict";je={operatorWriteEnabled:!1,operatorIds:[]}});var Hs,Os,js,wt,kt,$t,Vi,we,Ze,Et=y(()=>{"use strict";Hs={default_warning_days:[0,365],default_consumable_threshold:[1,90],battery_low_percent:[1,90],battery_recovered_percent:[20,100],archive_oneoff_days:[0,3650],delete_archived_oneoff_days:[0,3650],notify_due_soon_interval_hours:[0,720],notify_overdue_interval_hours:[0,720],notify_triggered_interval_hours:[0,720],max_notifications_per_day:[0,1e3],notification_bundle_threshold:[2,20],snooze_duration_hours:[1,168],warranty_reminder_days:[1,365],budget_alert_threshold:[10,100],currency_decimals:[0,3]},Os=[0,1440],js=[1,1e4],wt=[0,365],kt=15,$t=[.1,.9],Vi=[0,365],we=[.01,1e4],Ze=[.01,1e5]});function Xa(s){let[r,e]=Hs.default_warning_days,t=s?.general?.default_warning_days,i=s?.general?.row_action_style;return{features:{...ei.features,...s?.features??{}},defaultWarningDays:typeof t=="number"&&Number.isFinite(t)&&t>=r&&t<=e?t:ei.defaultWarningDays,rowActionStyle:Ja.includes(i??"")?i:ei.rowActionStyle,access:{operatorWriteEnabled:s?.operator_write_enabled===!0,operatorIds:Array.isArray(s?.admin_panel_user_ids)?s.admin_panel_user_ids.filter(a=>typeof a=="string"):[]},budget:s?.budget??null,rowActionNoticePending:s?.general?.row_action_notice_pending===!0,refsInLists:s?.general?.ref_numbers_in_lists===!0,objectsTableColumns:Array.isArray(s?.objects_table_columns)?s.objects_table_columns:void 0,partsCostMode:Wr(s?.budget?.parts_cost_mode)}}function Ds(){let s=window;return s.__msSettingsCache??={promise:null}}function ne(s){let r=Ds();if(r.promise)return r.promise;let e=s.connection.sendMessagePromise({type:"maintenance_supporter/settings"}).then(t=>Xa(t)).catch(()=>(r.promise===e&&(r.promise=null),ei));return r.promise=e,e}function Ms(){Ds().promise=null}var Ja,ei,St=y(()=>{"use strict";xt();Et();Mt();Ja=["buttons_compact","buttons","icons"];ei={features:{adaptive:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1},defaultWarnin
Line truncated
|
||||
`});function Xt(s){if(!s)return[];let r=[],e=s.photo_doc_id;typeof e=="string"&&r.push(e);let t=s.photo_doc_ids;Array.isArray(t)&&r.push(...t);let i=[];for(let a of r){if(typeof a!="string")continue;let o=a.trim();if(!(!o||i.includes(o))&&(i.push(o),i.length>=10))break}return i}var ei=y(()=>{"use strict"});function Cs(){let s=new Uint8Array(4);return(globalThis.crypto??{getRandomValues:r=>r.map(()=>Math.floor(Math.random()*256))}).getRandomValues(s),Array.from(s,r=>r.toString(16).padStart(2,"0")).join("")}function be(s){let r=s?.reading_values;if(!Array.isArray(r))return[];let e=[],t=new Set;for(let i of r){if(!i||typeof i!="object")continue;let a=i;typeof a.id!="string"||!a.id||t.has(a.id)||typeof a.value!="number"||!Number.isFinite(a.value)||(t.add(a.id),e.push({id:a.id,name:typeof a.name=="string"?a.name:a.id,unit:typeof a.unit=="string"?a.unit:null,value:a.value}))}return e}function Is(s){return s.filter(r=>r.type==="completed"&&(r.reading_value!=null||be(r).length>0)).sort((r,e)=>r.timestamp.localeCompare(e.timestamp))}function Rs(s){return Is(s??[]).map(r=>({timestamp:r.timestamp,values:be(r)})).filter(r=>r.values.length>0)}function Ps(s,r,e){let t;for(let i of s){if(e!==void 0){let o=Q(i.timestamp);if(!isNaN(o)&&o>=e)break}let a=i.values.find(o=>o.id===r);a&&(t=a)}return t}function Ls(s,r,e){let t=be(r).find(a=>a.id===e);if(!t)return null;let i=null;for(let a of Is(s)){if(a.timestamp>=r.timestamp)break;let o=be(a).find(d=>d.id===e);o&&(i=o.value)}return i==null?null:t.value-i}function Ns(s){let r=new Set,e=new Set;for(let t of s){let i=(t.name||"").trim().toLowerCase();i&&(r.has(i)?e.add(t.id):r.add(i))}return e}function qs(s){let r=[],e=new Set,t=new Set;for(let i of s){let a=(i.name||"").trim();if(!(!a||e.has(i.id)||t.has(a.toLowerCase()))&&(e.add(i.id),t.add(a.toLowerCase()),r.push({id:i.id,name:a,unit:(i.unit||"").trim()||null}),r.length>=Bi))break}return r}var Bi,Oe=y(()=>{"use strict";Z();Bi=20});function Wi(s,r,e,t){let i=e,a=p=>typeof p=="string"?p:null,o=p=>typeof p=="number"?p:null,d=a(i.timestamp)??"";return{entry_id:s,task_id:r,original_timestamp:d,type:a(i.type)||"completed",timestamp:d,notes:a(i.notes),cost:o(i.cost),duration:o(i.duration),completed_by:a(i.completed_by),used_parts:Array.isArray(i.used_parts)?i.used_parts:null,photo_doc_ids:Xt(i),reading_value:o(i.reading_value),reading_values:be(i),readings:t?.readings??[],task_type:t?.type??null,reading_unit:t?.reading_unit??null}}async function Hs(s,r,e,t){let a=(await s.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:r})).tasks?.find(d=>d.id===e);if(!a)return null;let o=a.history?.find(d=>d.timestamp===t);return o||(o=(await s.connection.sendMessagePromise({type:"maintenance_supporter/task/history",entry_id:r,task_id:e})).history?.find(p=>p.timestamp===t)),o?Wi(r,e,o,a):null}var Vi=y(()=>{"use strict";ei();Oe()});function xe(s,r=je){return s?s.is_admin?!0:r.operatorWriteEnabled&&r.operatorIds.includes(s.id):!1}var je,xt=y(()=>{"use strict";je={operatorWriteEnabled:!1,operatorIds:[]}});var Os,js,Ds,wt,kt,$t,Gi,we,Ze,Et=y(()=>{"use strict";Os={default_warning_days:[0,365],default_consumable_threshold:[1,90],battery_low_percent:[1,90],battery_recovered_percent:[20,100],archive_oneoff_days:[0,3650],delete_archived_oneoff_days:[0,3650],notify_due_soon_interval_hours:[0,720],notify_overdue_interval_hours:[0,720],notify_triggered_interval_hours:[0,720],max_notifications_per_day:[0,1e3],notification_bundle_threshold:[2,20],snooze_duration_hours:[1,168],warranty_reminder_days:[1,365],budget_alert_threshold:[10,100],currency_decimals:[0,3]},js=[0,1440],Ds=[1,1e4],wt=[0,365],kt=15,$t=[.1,.9],Gi=[0,365],we=[.01,1e4],Ze=[.01,1e5]});function ro(s){let[r,e]=Os.default_warning_days,t=s?.general?.default_warning_days,i=s?.general?.row_action_style;return{features:{...ti.features,...s?.features??{}},defaultWarningDays:typeof t=="number"&&Number.isFinite(t)&&t>=r&&t<=e?t:ti.defaultWarningDays,rowActionStyle:io.includes(i??"")?i:ti.rowActionStyle,access:{operatorWriteEnabled:s?.operator_write_enabled===!0,operatorIds:Array.isArray(s?.admin_panel_user_ids)?s.admin_panel_user_ids.filter(a=>typeof a=="string"):[]},budget:s?.budget??null,rowActionNoticePending:s?.general?.row_action_notice_pending===!0,refsInLists:s?.general?.ref_numbers_in_lists===!0,objectsTableColumns:Array.isArray(s?.objects_table_columns)?s.objects_table_columns:void 0,partsCostMode:Vr(s?.budget?.parts_cost_mode)}}function Ms(){let s=window;return s.__msSettingsCache??={promise:null}}function se(s){let r=Ms();if(r.promise)return r.promise;let e=s.connection.sendMessagePromise({type:"maintenance_supporter/settings"}).then(t=>ro(t)).catch(()=>(r.promise===e&&(r.promise=null),ti));return r.promise=e,e}function zs(){Ms().promise=null}var io,ti,St=y(()=>{"use strict";xt();Et();zt();io=["buttons_compact","buttons","icons"];ti={features:{adaptive:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1,places:!1},def
Line truncated
|
||||
<label class="field">
|
||||
${this.label?l`<span class="label">${this.label}${this.required?l`<span class="req">*</span>`:h}</span>`:h}
|
||||
${this.multiline?l`
|
||||
@@ -1347,7 +1347,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
color: var(--secondary-text-color);
|
||||
font-style: italic;
|
||||
}
|
||||
`,c([v()],D.prototype,"label",2),c([v()],D.prototype,"value",2),c([v()],D.prototype,"placeholder",2),c([v()],D.prototype,"type",2),c([v({type:Boolean})],D.prototype,"required",2),c([v({type:Boolean})],D.prototype,"disabled",2),c([v()],D.prototype,"step",2),c([v()],D.prototype,"min",2),c([v()],D.prototype,"max",2),c([v()],D.prototype,"pattern",2),c([v()],D.prototype,"helper",2),c([v({type:Boolean})],D.prototype,"multiline",2),c([v({type:Number})],D.prototype,"rows",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",D)});function ao(s){let e=O("2001-02-03",s).match(/\d+/g)||[],t=d=>e.findIndex(p=>Number(p)===d),i=t(2001),a=t(2),o=t(3);return i===0?"YMD":a>=0&&o>=0&&a<o?"MDY":"DMY"}function Je(s,r,e){if(s<1e3||s>9999||r<1||r>12||e<1||e>31)return null;let t=new Date(s,r-1,e);return t.getFullYear()!==s||t.getMonth()!==r-1||t.getDate()!==e?null:`${s}-${String(r).padStart(2,"0")}-${String(e).padStart(2,"0")}`}function Bs(s,r){let e=s.trim(),t;if(t=e.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/))return Je(+t[1],+t[2],+t[3]);if(t=e.match(/^(\d{4})$/))return Je(+t[1],1,1);if(t=e.match(/^(\d{1,2})[./](\d{4})$/))return Je(+t[2],+t[1],1);if(t=e.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/))return Je(+t[3],+t[2],+t[1]);if(t=e.match(/^(\d{1,2})[/-](\d{1,2})[/-](\d{4})$/)){let i=+t[1],a=+t[2],o=+t[3];return ao(r)==="MDY"?Je(o,i,a):Je(o,a,i)}return null}var Ws=y(()=>{"use strict";T()});function oo(s,r){if(r)switch(s){case"date":return r.split("T")[0];case"time":return r.length===5?`${r}:00`:r;case"datetime":{let[e,t="00:00:00"]=r.split("T");return`${e} ${t.length===5?`${t}:00`:t}`}}}function lo(s,r){if(typeof r!="string"||!r)return"";switch(s){case"date":return r.slice(0,10);case"time":return r.slice(0,5);case"datetime":{let[e,t="00:00:00"]=r.split(" ");return`${e}T${t.length===5?`${t}:00`:t}`}}}var U,Xe=y(()=>{"use strict";I();F();T();Ws();U=class extends S{constructor(){super(...arguments);this.kind="date";this.label="";this.value="";this.clearable=!1;this.disabled=!1;this.required=!1;this.lang="en";this._typing=!1;this._typed="";this._typedInvalid=!1}_startTyping(){this._typed=this.value,this._typedInvalid=!1,this._typing=!0,this.updateComplete.then(()=>this.shadowRoot?.querySelector("input.typed")?.focus())}_commitTyped(){if(!this._typed.trim()){this._typing=!1;return}let e=Bs(this._typed,this.lang);if(!e){this._typedInvalid=!0;return}this._typing=!1,this._typedInvalid=!1,this._emit(e)}_onTypedKey(e){e.key==="Enter"?(e.preventDefault(),this._commitTyped()):e.key==="Escape"&&(e.preventDefault(),this._typing=!1,this._typedInvalid=!1)}_selector(){switch(this.kind){case"date":return{date:{}};case"time":return{time:{no_second:!0}};case"datetime":return{datetime:{}}}}_onSelectorChange(e){e.stopPropagation(),this._emit(lo(this.kind,e.detail?.value))}_clear(){this._emit("")}_emit(e){e!==this.value&&(this.value=e,this.dispatchEvent(new CustomEvent("value-changed",{bubbles:!0,composed:!0,detail:{value:e}})))}render(){return l`
|
||||
`,c([v()],D.prototype,"label",2),c([v()],D.prototype,"value",2),c([v()],D.prototype,"placeholder",2),c([v()],D.prototype,"type",2),c([v({type:Boolean})],D.prototype,"required",2),c([v({type:Boolean})],D.prototype,"disabled",2),c([v()],D.prototype,"step",2),c([v()],D.prototype,"min",2),c([v()],D.prototype,"max",2),c([v()],D.prototype,"pattern",2),c([v()],D.prototype,"helper",2),c([v({type:Boolean})],D.prototype,"multiline",2),c([v({type:Number})],D.prototype,"rows",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",D)});function po(s){let e=O("2001-02-03",s).match(/\d+/g)||[],t=d=>e.findIndex(p=>Number(p)===d),i=t(2001),a=t(2),o=t(3);return i===0?"YMD":a>=0&&o>=0&&a<o?"MDY":"DMY"}function Je(s,r,e){if(s<1e3||s>9999||r<1||r>12||e<1||e>31)return null;let t=new Date(s,r-1,e);return t.getFullYear()!==s||t.getMonth()!==r-1||t.getDate()!==e?null:`${s}-${String(r).padStart(2,"0")}-${String(e).padStart(2,"0")}`}function Ws(s,r){let e=s.trim(),t;if(t=e.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/))return Je(+t[1],+t[2],+t[3]);if(t=e.match(/^(\d{4})$/))return Je(+t[1],1,1);if(t=e.match(/^(\d{1,2})[./](\d{4})$/))return Je(+t[2],+t[1],1);if(t=e.match(/^(\d{1,2})\.(\d{1,2})\.(\d{4})$/))return Je(+t[3],+t[2],+t[1]);if(t=e.match(/^(\d{1,2})[/-](\d{1,2})[/-](\d{4})$/)){let i=+t[1],a=+t[2],o=+t[3];return po(r)==="MDY"?Je(o,i,a):Je(o,a,i)}return null}var Vs=y(()=>{"use strict";T()});function uo(s,r){if(r)switch(s){case"date":return r.split("T")[0];case"time":return r.length===5?`${r}:00`:r;case"datetime":{let[e,t="00:00:00"]=r.split("T");return`${e} ${t.length===5?`${t}:00`:t}`}}}function ho(s,r){if(typeof r!="string"||!r)return"";switch(s){case"date":return r.slice(0,10);case"time":return r.slice(0,5);case"datetime":{let[e,t="00:00:00"]=r.split(" ");return`${e}T${t.length===5?`${t}:00`:t}`}}}var U,Xe=y(()=>{"use strict";I();F();T();Vs();U=class extends S{constructor(){super(...arguments);this.kind="date";this.label="";this.value="";this.clearable=!1;this.disabled=!1;this.required=!1;this.lang="en";this._typing=!1;this._typed="";this._typedInvalid=!1}_startTyping(){this._typed=this.value,this._typedInvalid=!1,this._typing=!0,this.updateComplete.then(()=>this.shadowRoot?.querySelector("input.typed")?.focus())}_commitTyped(){if(!this._typed.trim()){this._typing=!1;return}let e=Ws(this._typed,this.lang);if(!e){this._typedInvalid=!0;return}this._typing=!1,this._typedInvalid=!1,this._emit(e)}_onTypedKey(e){e.key==="Enter"?(e.preventDefault(),this._commitTyped()):e.key==="Escape"&&(e.preventDefault(),this._typing=!1,this._typedInvalid=!1)}_selector(){switch(this.kind){case"date":return{date:{}};case"time":return{time:{no_second:!0}};case"datetime":return{datetime:{}}}}_onSelectorChange(e){e.stopPropagation(),this._emit(ho(this.kind,e.detail?.value))}_clear(){this._emit("")}_emit(e){e!==this.value&&(this.value=e,this.dispatchEvent(new CustomEvent("value-changed",{bubbles:!0,composed:!0,detail:{value:e}})))}render(){return l`
|
||||
<div class="field">
|
||||
${this.label?l`<span class="label">${this.label}${this.required?l`<span class="req">*</span>`:h}</span>`:h}
|
||||
<div class="row">
|
||||
@@ -1360,7 +1360,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
@blur=${this._commitTyped} />`:l`<ha-selector
|
||||
.hass=${this.hass}
|
||||
.selector=${this._selector()}
|
||||
.value=${oo(this.kind,this.value)}
|
||||
.value=${uo(this.kind,this.value)}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._onSelectorChange}
|
||||
@@ -1433,7 +1433,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
}
|
||||
input.typed[aria-invalid="true"] { border-color: var(--error-color, #f44336); }
|
||||
input.typed:focus { outline: 2px solid var(--primary-color); outline-offset: -1px; }
|
||||
`,c([v({attribute:!1})],U.prototype,"hass",2),c([v()],U.prototype,"kind",2),c([v()],U.prototype,"label",2),c([v()],U.prototype,"value",2),c([v()],U.prototype,"helper",2),c([v({type:Boolean})],U.prototype,"clearable",2),c([v({type:Boolean})],U.prototype,"disabled",2),c([v({type:Boolean})],U.prototype,"required",2),c([v()],U.prototype,"lang",2),c([u()],U.prototype,"_typing",2),c([u()],U.prototype,"_typed",2),c([u()],U.prototype,"_typedInvalid",2);customElements.get("ms-date-field")||customElements.define("ms-date-field",U)});var P,Vs=y(()=>{"use strict";I();F();T();De();Gi();Xe();P=class extends S{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;this._replacing=!1;this._oldDeviceId="";this._replaceDevice="keep"}get _lang(){return N(this.hass)}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._replacing=!1,this._error="",this._open=!0}openReplace(e,t){this._entryId=e,this._replacing=!0,this._name=t.name||"",this._oldDeviceId=t.ha_device_id||"",this._haDeviceId="",this._replaceDevice="keep",this._error="",this._open=!0}openEdit(e,t){this._entryId=e,this._name=t.name||"",this._manufacturer=t.manufacturer||"",this._model=t.model||"",this._serialNumber=t.serial_number||"",this._areaId=t.area_id||"",this._installationDate=t.installation_date||"",this._warrantyExpiry=t.warranty_expiry||"",this._documentationUrl=t.documentation_url||"",this._notes=t.notes||"",this._haDeviceId=t.ha_device_id||"",this._parentEntryId=t.parent_entry_id||"",this._replacing=!1,this._error="",this._open=!0}async _replace(){if(this._loading||!this._entryId)return;let e={type:"maintenance_supporter/object/replace",entry_id:this._entryId,name:this._name.trim()||null};this._oldDeviceId?(this._replaceDevice==="other"&&(e.ha_device_id=this._haDeviceId),this._replaceDevice==="none"&&(e.ha_device_id=null)):this._haDeviceId&&(e.ha_device_id=this._haDeviceId);let t=await j(this,e,{busy:i=>{this._loading=i},fallbackKey:"save_error",onError:i=>{this._error=i}});t!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-replaced",{detail:{entry_id:t?.entry_id,device_swap:t?.device_swap}})))}_deviceName(e){let i=this.hass?.devices?.[e];return i?.name_by_user||i?.name||e}_deviceSelector(e){return l`<ha-form
|
||||
`,c([v({attribute:!1})],U.prototype,"hass",2),c([v()],U.prototype,"kind",2),c([v()],U.prototype,"label",2),c([v()],U.prototype,"value",2),c([v()],U.prototype,"helper",2),c([v({type:Boolean})],U.prototype,"clearable",2),c([v({type:Boolean})],U.prototype,"disabled",2),c([v({type:Boolean})],U.prototype,"required",2),c([v()],U.prototype,"lang",2),c([u()],U.prototype,"_typing",2),c([u()],U.prototype,"_typed",2),c([u()],U.prototype,"_typedInvalid",2);customElements.get("ms-date-field")||customElements.define("ms-date-field",U)});var R,Gs=y(()=>{"use strict";I();F();T();De();Ki();Xe();R=class extends S{constructor(){super(...arguments);this.objects=[];this.placesEnabled=!1;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._place="";this._remindOnSite=!1;this._entryId=null;this._replacing=!1;this._oldDeviceId="";this._replaceDevice="keep"}get _lang(){return N(this.hass)}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._place="",this._remindOnSite=!1,this._replacing=!1,this._error="",this._open=!0}openReplace(e,t){this._entryId=e,this._replacing=!0,this._name=t.name||"",this._oldDeviceId=t.ha_device_id||"",this._haDeviceId="",this._replaceDevice="keep",this._error="",this._open=!0}openEdit(e,t){this._entryId=e,this._name=t.name||"",this._manufacturer=t.manufacturer||"",this._model=t.model||"",this._serialNumber=t.serial_number||"",this._areaId=t.area_id||"",this._installationDate=t.installation_date||"",this._warrantyExpiry=t.warranty_expiry||"",this._documentationUrl=t.documentation_url||"",this._notes=t.notes||"",this._haDeviceId=t.ha_device_id||"",this._parentEntryId=t.parent_entry_id||"",this._place=t.place||"",this._remindOnSite=!!t.remind_on_site,this._replacing=!1,this._error="",this._open=!0}async _replace(){if(this._loading||!this._entryId)return;let e={type:"maintenance_supporter/object/replace",entry_id:this._entryId,name:this._name.trim()||null};this._oldDeviceId?(this._replaceDevice==="other"&&(e.ha_device_id=this._haDeviceId),this._replaceDevice==="none"&&(e.ha_device_id=null)):this._haDeviceId&&(e.ha_device_id=this._haDeviceId);let t=await j(this,e,{busy:i=>{this._loading=i},fallbackKey:"save_error",onError:i=>{this._error=i}});t!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-replaced",{detail:{entry_id:t?.entry_id,device_swap:t?.device_swap}})))}_deviceName(e){let i=this.hass?.devices?.[e];return i?.name_by_user||i?.name||e}_deviceSelector(e){return l`<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${{device:this._haDeviceId||void 0}}
|
||||
.schema=${[{name:"device",selector:{device:{}}}]}
|
||||
@@ -1477,7 +1477,26 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}async _save(){if(this._loading||!this._name.trim())return;this._error="";let e={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},t=await j(this,this._entryId?{type:"maintenance_supporter/object/update",entry_id:this._entryId,...e}:{type:"maintenance_supporter/object/create",...e},{busy:i=>{this._loading=i},fallbackKey:"save_error",onError:i=>{this._error=i}});t!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-saved",{detail:{device_swap:t?.device_swap}})))}_parentChoices(){return(this.objects||[]).filter(e=>e.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return l``;if(this._replacing)return this._renderReplace();let e=this._lang,t=this._entryId?n("edit_object",e):n("new_object",e);return l`
|
||||
`}_renderPlace(e){let t=!!this._place&&!this.hass?.states?.[this._place];return l`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${{place:this._place||void 0}}
|
||||
.schema=${[{name:"place",selector:{entity:{domain:"zone"}}}]}
|
||||
.computeLabel=${()=>n("place_optional",e)}
|
||||
@value-changed=${i=>{this._place=i.detail.value?.place||"",this._place||(this._remindOnSite=!1)}}
|
||||
></ha-form>
|
||||
${t?l`<div class="place-missing">${n("place_missing",e)}</div>`:h}
|
||||
<label class="place-on-site ${this._place?"":"disabled"}">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${this._remindOnSite}
|
||||
?disabled=${!this._place}
|
||||
@change=${i=>this._remindOnSite=i.target.checked}
|
||||
/>
|
||||
<span>${n("remind_on_site",e)}</span>
|
||||
</label>
|
||||
${this._place?l`<div class="place-hint">${n("remind_on_site_hint",e)}</div>`:h}
|
||||
`}async _save(){if(this._loading||!this._name.trim())return;this._error="";let e={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.placesEnabled?{place:this._place||null,remind_on_site:!!this._place&&this._remindOnSite}:{}},t=await j(this,this._entryId?{type:"maintenance_supporter/object/update",entry_id:this._entryId,...e}:{type:"maintenance_supporter/object/create",...e},{busy:i=>{this._loading=i},fallbackKey:"save_error",onError:i=>{this._error=i}});t!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-saved",{detail:{device_swap:t?.device_swap}})))}_parentChoices(){return(this.objects||[]).filter(e=>e.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return l``;if(this._replacing)return this._renderReplace();let e=this._lang,t=this._entryId?n("edit_object",e):n("new_object",e);return l`
|
||||
<ha-dialog open @closed=${this._close}>
|
||||
<div class="dialog-title">${t}</div>
|
||||
<div class="content">
|
||||
@@ -1515,6 +1534,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._areaId}
|
||||
@value-changed=${i=>this._areaId=i.detail.value||""}
|
||||
></ha-area-picker>
|
||||
${this.placesEnabled?this._renderPlace(e):h}
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
@@ -1578,7 +1598,26 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}};P.styles=E`
|
||||
`}};R.styles=E`
|
||||
.place-on-site {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.place-on-site.disabled {
|
||||
color: var(--disabled-text-color, var(--secondary-text-color));
|
||||
}
|
||||
.place-hint,
|
||||
.place-missing {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
margin-top: -4px;
|
||||
}
|
||||
.place-missing {
|
||||
color: var(--warning-color, #c77700);
|
||||
}
|
||||
.dialog-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
@@ -1648,7 +1687,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.radio-row input {
|
||||
margin: 0;
|
||||
}
|
||||
`,c([v({attribute:!1})],P.prototype,"hass",2),c([v({attribute:!1})],P.prototype,"objects",2),c([u()],P.prototype,"_open",2),c([u()],P.prototype,"_loading",2),c([u()],P.prototype,"_error",2),c([u()],P.prototype,"_name",2),c([u()],P.prototype,"_manufacturer",2),c([u()],P.prototype,"_model",2),c([u()],P.prototype,"_serialNumber",2),c([u()],P.prototype,"_areaId",2),c([u()],P.prototype,"_installationDate",2),c([u()],P.prototype,"_warrantyExpiry",2),c([u()],P.prototype,"_documentationUrl",2),c([u()],P.prototype,"_notes",2),c([u()],P.prototype,"_haDeviceId",2),c([u()],P.prototype,"_parentEntryId",2),c([u()],P.prototype,"_entryId",2),c([u()],P.prototype,"_replacing",2),c([u()],P.prototype,"_oldDeviceId",2),c([u()],P.prototype,"_replaceDevice",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",P)});function co(s){let r=(s||"").split(/\s+/).filter(Boolean);return r.length===0?"?":r.length===1?r[0][0].toUpperCase():(r[0][0]+r[r.length-1][0]).toUpperCase()}function po(s){let r=0;for(let e of s)r=r*31+e.charCodeAt(0)>>>0;return Gs[r%Gs.length]}function Ks(s){return s?{id:s.id,name:s.name,initials:s.initials||co(s.name),color:s.color||po(s.id)}:null}var Gs,Ys=y(()=>{"use strict";I();Gs=["#c62828","#ad1457","#6a1b9a","#4527a0","#283593","#1565c0","#00838f","#2e7d32","#558b2f","#ef6c00","#6d4c41","#546e7a"]});var ti,Qs=y(()=>{"use strict";Ys();ti=class{constructor(r){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=r}updateHass(r){this.hass=r}async getUsers(r=!1){let e=Date.now();if(!r&&this.usersCache&&e-this.cacheTimestamp<this.CACHE_TTL_MS)return this.usersCache;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/users/list"});return this.usersCache=t.users,this.cacheTimestamp=e,this.usersCache}catch(t){return console.error("Failed to fetch users:",t),this.usersCache||[]}}async assignUser(r,e,t){await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/assign_user",entry_id:r,task_id:e,user_id:t})}async getTasksByUser(r){return(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tasks/by_user",user_id:r})).tasks}getUserName(r){return!r||!this.usersCache?null:this.usersCache.find(t=>t.id===r)?.name||null}getPerson(r){return Ks(this.getUser(r))}getUser(r){return!r||!this.usersCache?null:this.usersCache.find(e=>e.id===r)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(r){return r?r===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}}});function ae(s){return`${s.entry_id??""}\0${s.part_id}`}function Zs(s,r,e,t){let i=!!s.entry_id&&s.entry_id!==r,a=i?s.entry_id:r,o=e.find(m=>m.entry_id===a),d=(o?.parts||[]).find(m=>m.id===s.part_id)||null,p=i&&o?.object?.name||"",_=d?.name||n("shared_part_unknown",t);return{part:d,foreign:i,ownerName:p,label:p?`${_} (${p})`:_}}function Js(s,r,e,t){let{part:i,label:a}=Zs(s,r,e,t),o=i&&i.stock!==null&&i.stock!==void 0?` (${Ye(i.stock,i.unit,t)})`:"",d=i?.storage_location?` \u2014 ${i.storage_location}`:"";return`${C(s.quantity,t)}\xD7 ${a}${o}${d}`}function Xs(s,r,e,t){let a=(e.find(d=>d.entry_id===r)?.parts||[]).map(d=>({...d})),o=new Set(a.map(d=>ae({part_id:d.id})));for(let d of s?.consumes_parts||[]){if(!d.entry_id||d.entry_id===r)continue;let p=ae(d);if(o.has(p))continue;o.add(p);let{part:_,ownerName:m}=Zs(d,r,e,t);a.push({id:d.part_id,name:_?.name||n("shared_part_unknown",t),unit:_?.unit,stock:_?.stock??null,storage_location:_?.storage_location,entry_id:d.entry_id,owner_name:m})}return a}var ii=y(()=>{"use strict";T()});var Ki,en,tn,rn=y(()=>{"use strict";Ki=["sensor","binary_sensor","number","input_number","input_boolean","switch","climate","vacuum","cover","fan","light","water_heater","humidifier","media_player","weather","air_quality","valve","lawn_mower","lock"],en=["sensor"],tn=["temperature","humidity","pressure"]});function et(s){let r=(s??"").trim();if(r==="")return null;let e=Number(r.replace(",","."));return!Number.isFinite(e)||e<0?null:Math.round(e)}var ri=y(()=>{"use strict"});var Yi,Tt,Qi=y(()=>{"use strict";Yi=["notes","cost","duration","photo","user"],Tt={notes:"notes_label",cost:"cost",duration:"duration",photo:"photo_label",user:"user_label"}});var uo,ho,Zi,sn=y(()=>{"use strict";uo={cleaning:"mdi:broom",inspection:"mdi:magnify",replacement:"mdi:swap-horizontal",calibration:"mdi:tune",service:"mdi:wrench",reading:"mdi:counter",custom:"mdi:wrench-clock"},ho="mdi:wrench-clock",Zi=s=>s&&uo[s]||ho});var ke,si=y(()=>{"use strict";I();F();T();ke=class extends S{constructor(){super(...arguments);this.value="";this.lang="en";this._credit=!1;this._amount=""}willUpdate(e){if(!e.has("value"))return;let t=(this.value??"").trim();if(t===this._signed())return;let i=parseFloat(t);if(t===""||Number.isNaN(i)){this._amount="",this._credit=!1;return}this._credit=i<0,this._amount=String(Math.abs(i))}_signed(){let e=this._amount.trim();return e===""?"":this._credit?`-${e}`:e}_emit
Line truncated
|
||||
`,c([v({attribute:!1})],R.prototype,"hass",2),c([v({attribute:!1})],R.prototype,"objects",2),c([v({type:Boolean})],R.prototype,"placesEnabled",2),c([u()],R.prototype,"_open",2),c([u()],R.prototype,"_loading",2),c([u()],R.prototype,"_error",2),c([u()],R.prototype,"_name",2),c([u()],R.prototype,"_manufacturer",2),c([u()],R.prototype,"_model",2),c([u()],R.prototype,"_serialNumber",2),c([u()],R.prototype,"_areaId",2),c([u()],R.prototype,"_installationDate",2),c([u()],R.prototype,"_warrantyExpiry",2),c([u()],R.prototype,"_documentationUrl",2),c([u()],R.prototype,"_notes",2),c([u()],R.prototype,"_haDeviceId",2),c([u()],R.prototype,"_parentEntryId",2),c([u()],R.prototype,"_place",2),c([u()],R.prototype,"_remindOnSite",2),c([u()],R.prototype,"_entryId",2),c([u()],R.prototype,"_replacing",2),c([u()],R.prototype,"_oldDeviceId",2),c([u()],R.prototype,"_replaceDevice",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",R)});function _o(s){let r=(s||"").split(/\s+/).filter(Boolean);return r.length===0?"?":r.length===1?r[0][0].toUpperCase():(r[0][0]+r[r.length-1][0]).toUpperCase()}function mo(s){let r=0;for(let e of s)r=r*31+e.charCodeAt(0)>>>0;return Ks[r%Ks.length]}function Ys(s){return s?{id:s.id,name:s.name,initials:s.initials||_o(s.name),color:s.color||mo(s.id)}:null}var Ks,Qs=y(()=>{"use strict";I();Ks=["#c62828","#ad1457","#6a1b9a","#4527a0","#283593","#1565c0","#00838f","#2e7d32","#558b2f","#ef6c00","#6d4c41","#546e7a"]});var ii,Zs=y(()=>{"use strict";Qs();ii=class{constructor(r){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=r}updateHass(r){this.hass=r}async getUsers(r=!1){let e=Date.now();if(!r&&this.usersCache&&e-this.cacheTimestamp<this.CACHE_TTL_MS)return this.usersCache;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/users/list"});return this.usersCache=t.users,this.cacheTimestamp=e,this.usersCache}catch(t){return console.error("Failed to fetch users:",t),this.usersCache||[]}}async assignUser(r,e,t){await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/assign_user",entry_id:r,task_id:e,user_id:t})}async getTasksByUser(r){return(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tasks/by_user",user_id:r})).tasks}getUserName(r){return!r||!this.usersCache?null:this.usersCache.find(t=>t.id===r)?.name||null}getPerson(r){return Ys(this.getUser(r))}getUser(r){return!r||!this.usersCache?null:this.usersCache.find(e=>e.id===r)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(r){return r?r===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}}});function ae(s){return`${s.entry_id??""}\0${s.part_id}`}function Js(s,r,e,t){let i=!!s.entry_id&&s.entry_id!==r,a=i?s.entry_id:r,o=e.find(m=>m.entry_id===a),d=(o?.parts||[]).find(m=>m.id===s.part_id)||null,p=i&&o?.object?.name||"",_=d?.name||n("shared_part_unknown",t);return{part:d,foreign:i,ownerName:p,label:p?`${_} (${p})`:_}}function Xs(s,r,e,t){let{part:i,label:a}=Js(s,r,e,t),o=i&&i.stock!==null&&i.stock!==void 0?` (${Ye(i.stock,i.unit,t)})`:"",d=i?.storage_location?` \u2014 ${i.storage_location}`:"";return`${C(s.quantity,t)}\xD7 ${a}${o}${d}`}function en(s,r,e,t){let a=(e.find(d=>d.entry_id===r)?.parts||[]).map(d=>({...d})),o=new Set(a.map(d=>ae({part_id:d.id})));for(let d of s?.consumes_parts||[]){if(!d.entry_id||d.entry_id===r)continue;let p=ae(d);if(o.has(p))continue;o.add(p);let{part:_,ownerName:m}=Js(d,r,e,t);a.push({id:d.part_id,name:_?.name||n("shared_part_unknown",t),unit:_?.unit,stock:_?.stock??null,storage_location:_?.storage_location,entry_id:d.entry_id,owner_name:m})}return a}var ri=y(()=>{"use strict";T()});var Yi,tn,rn,sn=y(()=>{"use strict";Yi=["sensor","binary_sensor","number","input_number","input_boolean","switch","climate","vacuum","cover","fan","light","water_heater","humidifier","media_player","weather","air_quality","valve","lawn_mower","lock"],tn=["sensor"],rn=["temperature","humidity","pressure"]});function et(s){let r=(s??"").trim();if(r==="")return null;let e=Number(r.replace(",","."));return!Number.isFinite(e)||e<0?null:Math.round(e)}var si=y(()=>{"use strict"});var Qi,Tt,Zi=y(()=>{"use strict";Qi=["notes","cost","duration","photo","user"],Tt={notes:"notes_label",cost:"cost",duration:"duration",photo:"photo_label",user:"user_label"}});var go,fo,Ji,nn=y(()=>{"use strict";go={cleaning:"mdi:broom",inspection:"mdi:magnify",replacement:"mdi:swap-horizontal",calibration:"mdi:tune",service:"mdi:wrench",reading:"mdi:counter",custom:"mdi:wrench-clock"},fo="mdi:wrench-clock",Ji=s=>s&&go[s]||fo});function ni(s){return s?Array.isArray(s.entity_ids)&&s.entity_ids.length>0?[...s.entity_ids]:s.entity_id?[s.entity_id]:[]:[]}function an(s){if(!(!s||s.type==="compound"))return ni(s)[0]}var on=y(()=>{"use strict"});var ke,ai=y(()=>{"use strict";I();F();T();ke=class extends S{constructor(){super(...arguments);this.value="";this.lang="en";this._cred
Line truncated
|
||||
<div class="wrap">
|
||||
<div class="sign" role="group">
|
||||
<button type="button" class="kind cost ${this._credit?"":"on"}" aria-pressed=${String(!this._credit)}
|
||||
@@ -1683,9 +1722,9 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.kind.credit.on { background: var(--success-color, #43a047); }
|
||||
.amount { flex: 1; min-width: 0; }
|
||||
.hint { margin-top: 4px; font-size: 12px; color: var(--secondary-text-color); }
|
||||
`],c([v()],ke.prototype,"value",2),c([v()],ke.prototype,"lang",2),c([u()],ke.prototype,"_credit",2),c([u()],ke.prototype,"_amount",2);customElements.get("ms-cost-input")||customElements.define("ms-cost-input",ke)});function on(){return{entityIds:"",type:"threshold",attribute:"",above:"",below:"",equals:"",notEquals:"",forMinutes:"0",targetValue:"",deltaMode:!1,fromState:"",toState:"",targetChanges:"",runtimeHours:"",onStates:"",daysBefore:"0",carry:{}}}function cn(s){return{type:s.type||"threshold",attribute:s.attribute||"",above:s.trigger_above?.toString()??"",below:s.trigger_below?.toString()??"",equals:s.trigger_equals?.toString()??"",notEquals:s.trigger_not_equals?.toString()??"",forMinutes:s.trigger_for_minutes?.toString()??"0",targetValue:s.trigger_target_value?.toString()??"",deltaMode:s.trigger_delta_mode||!1,fromState:s.trigger_from_state||"",toState:s.trigger_to_state||"",targetChanges:s.trigger_target_changes?.toString()??"",runtimeHours:s.trigger_runtime_hours?.toString()??"",onStates:(s.trigger_on_states||[]).join(", "),daysBefore:s.trigger_days_before?.toString()??"0"}}function dn(s,r){r.attribute&&(s.attribute=r.attribute);let e=parseInt(r.forMinutes,10);if(r.type==="threshold"){let t=parseFloat(r.above);isNaN(t)||(s.trigger_above=t);let i=parseFloat(r.below);isNaN(i)||(s.trigger_below=i);let a=parseFloat(r.equals);isNaN(a)||(s.trigger_equals=a);let o=parseFloat(r.notEquals);isNaN(o)||(s.trigger_not_equals=o),isNaN(e)||(s.trigger_for_minutes=e)}else if(r.type==="counter"){let t=parseFloat(r.targetValue);isNaN(t)||(s.trigger_target_value=t),s.trigger_delta_mode=r.deltaMode}else if(r.type==="state_change"){r.fromState&&(s.trigger_from_state=r.fromState),r.toState&&(s.trigger_to_state=r.toState);let t=parseInt(r.targetChanges,10);isNaN(t)||(s.trigger_target_changes=t),isNaN(e)||(s.trigger_for_minutes=e)}else if(r.type==="runtime"){let t=parseFloat(r.runtimeHours);isNaN(t)||(s.trigger_runtime_hours=t);let i=(r.onStates||"").split(",").map(a=>a.trim()).filter(Boolean);i.length>0&&(s.trigger_on_states=i)}else if(r.type==="due_date"){let t=parseInt(r.daysBefore,10);s.trigger_days_before=isNaN(t)?0:t}}function yo(s){return{entityIds:(s.entity_ids||(s.entity_id?[s.entity_id]:[])).join(", "),...cn(s),carry:Object.fromEntries(Object.entries(s).filter(([e])=>!vo.has(e)&&!e.startsWith("_")))}}function bo(s){let r=s.entityIds.split(",").map(t=>t.trim()).filter(Boolean);if(r.length===0)return null;let e={...s.carry||{},entity_id:r[0],entity_ids:r,type:s.type};return dn(e,s),e}function xo(s){return Array.from({length:7},(r,e)=>Ke(e,s,"short"))}function wo(s){return Array.from({length:12},(r,e)=>Fi(e,s,"short"))}var _o,mo,go,ni,nn,ln,an,fo,de,Me,vo,g,Ji,pn=y(()=>{"use strict";I();F();T();Qs();ii();Oe();rn();De();Et();ri();Qi();Gi();sn();Xe();si();_o=["cleaning","inspection","replacement","calibration","service","reading","custom"],mo=["low","normal","high"],go=["time_based","weekdays","nth_weekday","day_of_month","calendar","sensor_based","one_time","manual"],ni=["weekdays","nth_weekday","day_of_month","calendar"],nn=["time_based","one_time",...ni],ln=["threshold","counter","state_change","runtime","due_date"],an=(s,r)=>n(s==="due_date"?"trigger_type_due_date":s,r),fo=[...ln,"compound"],de={alpha:"0.3",min:"7",max:"365"},Me=we;vo=new Set(["entity_id","entity_ids","type","attribute","trigger_above","trigger_below","trigger_equals","trigger_not_equals","trigger_for_minutes","trigger_target_value","trigger_delta_mode","trigger_from_state","trigger_to_state","trigger_target_changes","trigger_runtime_hours","trigger_on_states","trigger_days_before"]);g=class g extends S{constructor(){super(...arguments);this.checklistsEnabled=!1;this.scheduleTimeEnabled=!1;this.completionActionsEnabled=!1;this.adaptiveFeature=!1;this.seasonalFeature=!1;this.environmentalFeature=!1;this.defaultWarningDays=7;this.parts=[];this._foreignOwners=[];this._open=!1;this._entityPickerFallback=!1;this._pickerProbeStrikes=0;this._loading=!1;this._error="";this._warning="";this._entryId="";this._taskId=null;this._isFleetTask=!1;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._calendarEntity="";this._seasonMonths=[];this._endsMode="never";this._endsCount="";this._endsUntil="";this._schedulePreview=[];this._schedulePreviewEnded=!1;this._previewSeq=0;this._notes="";this._documentationUrl="";this._customIcon="";this._notifyIcon="";this._priority="normal";this._labels="";this._mirrorTodoEntities=[];this._enabled=!0;this._triggerEntityId="";this._triggerEntityIds=[];this._triggerEntityLogic="any";this._triggerAttribute="";this._triggerType="threshold";this._triggerAbove="";this._triggerBelow="";this._triggerEquals="";t
Line truncated
|
||||
`],c([v()],ke.prototype,"value",2),c([v()],ke.prototype,"lang",2),c([u()],ke.prototype,"_credit",2),c([u()],ke.prototype,"_amount",2);customElements.get("ms-cost-input")||customElements.define("ms-cost-input",ke)});function dn(){return{entityIds:"",type:"threshold",attribute:"",above:"",below:"",equals:"",notEquals:"",forMinutes:"0",targetValue:"",deltaMode:!1,fromState:"",toState:"",targetChanges:"",runtimeHours:"",onStates:"",daysBefore:"0",carry:{}}}function un(s){return{type:s.type||"threshold",attribute:s.attribute||"",above:s.trigger_above?.toString()??"",below:s.trigger_below?.toString()??"",equals:s.trigger_equals?.toString()??"",notEquals:s.trigger_not_equals?.toString()??"",forMinutes:s.trigger_for_minutes?.toString()??"0",targetValue:s.trigger_target_value?.toString()??"",deltaMode:s.trigger_delta_mode||!1,fromState:s.trigger_from_state||"",toState:s.trigger_to_state||"",targetChanges:s.trigger_target_changes?.toString()??"",runtimeHours:s.trigger_runtime_hours?.toString()??"",onStates:(s.trigger_on_states||[]).join(", "),daysBefore:s.trigger_days_before?.toString()??"0"}}function hn(s,r){r.attribute&&(s.attribute=r.attribute);let e=parseInt(r.forMinutes,10);if(r.type==="threshold"){let t=parseFloat(r.above);isNaN(t)||(s.trigger_above=t);let i=parseFloat(r.below);isNaN(i)||(s.trigger_below=i);let a=parseFloat(r.equals);isNaN(a)||(s.trigger_equals=a);let o=parseFloat(r.notEquals);isNaN(o)||(s.trigger_not_equals=o),isNaN(e)||(s.trigger_for_minutes=e)}else if(r.type==="counter"){let t=parseFloat(r.targetValue);isNaN(t)||(s.trigger_target_value=t),s.trigger_delta_mode=r.deltaMode}else if(r.type==="state_change"){r.fromState&&(s.trigger_from_state=r.fromState),r.toState&&(s.trigger_to_state=r.toState);let t=parseInt(r.targetChanges,10);isNaN(t)||(s.trigger_target_changes=t),isNaN(e)||(s.trigger_for_minutes=e)}else if(r.type==="runtime"){let t=parseFloat(r.runtimeHours);isNaN(t)||(s.trigger_runtime_hours=t);let i=(r.onStates||"").split(",").map(a=>a.trim()).filter(Boolean);i.length>0&&(s.trigger_on_states=i)}else if(r.type==="due_date"){let t=parseInt(r.daysBefore,10);s.trigger_days_before=isNaN(t)?0:t}}function ko(s){return{entityIds:ni(s).join(", "),...un(s),carry:Object.fromEntries(Object.entries(s).filter(([e])=>!wo.has(e)&&!e.startsWith("_")))}}function $o(s){let r=s.entityIds.split(",").map(t=>t.trim()).filter(Boolean);if(r.length===0)return null;let e={...s.carry||{},entity_id:r[0],entity_ids:r,type:s.type};return hn(e,s),e}function Eo(s){return Array.from({length:7},(r,e)=>Ke(e,s,"short"))}function So(s){return Array.from({length:12},(r,e)=>Ui(e,s,"short"))}var vo,yo,bo,oi,ln,pn,cn,xo,de,Me,wo,g,Xi,_n=y(()=>{"use strict";I();F();T();Zs();ri();Oe();sn();De();Et();si();Zi();Ki();nn();on();Xe();ai();vo=["cleaning","inspection","replacement","calibration","service","reading","custom"],yo=["low","normal","high"],bo=["time_based","weekdays","nth_weekday","day_of_month","calendar","sensor_based","one_time","manual"],oi=["weekdays","nth_weekday","day_of_month","calendar"],ln=["time_based","one_time",...oi],pn=["threshold","counter","state_change","runtime","due_date"],cn=(s,r)=>n(s==="due_date"?"trigger_type_due_date":s,r),xo=[...pn,"compound"],de={alpha:"0.3",min:"7",max:"365"},Me=we;wo=new Set(["entity_id","entity_ids","type","attribute","trigger_above","trigger_below","trigger_equals","trigger_not_equals","trigger_for_minutes","trigger_target_value","trigger_delta_mode","trigger_from_state","trigger_to_state","trigger_target_changes","trigger_runtime_hours","trigger_on_states","trigger_days_before"]);g=class g extends S{constructor(){super(...arguments);this.checklistsEnabled=!1;this.scheduleTimeEnabled=!1;this.completionActionsEnabled=!1;this.adaptiveFeature=!1;this.seasonalFeature=!1;this.environmentalFeature=!1;this.defaultWarningDays=7;this.parts=[];this._foreignOwners=[];this._open=!1;this._entityPickerFallback=!1;this._pickerProbeStrikes=0;this._loading=!1;this._error="";this._warning="";this._entryId="";this._taskId=null;this._isFleetTask=!1;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._calendarEntity="";this._seasonMonths=[];this._endsMode="never";this._endsCount="";this._endsUntil="";this._schedulePreview=[];this._schedulePreviewEnded=!1;this._previewSeq=0;this._notes="";this._documentationUrl="";this._customIcon="";this._notifyIcon="";this._priority="normal";this._labels="";this._mirrorTodoEntities=[];this._enabled=!0;this._triggerEntityId="";this._triggerEntityIds=[];this._triggerEntityLogic="any";this._triggerAttribute="";this._triggerType="threshold";this._triggerAbove="";this._triggerBelow="";this._triggerEquals="";this._triggerNotEquals="";this._trigg
Line truncated
|
||||
`),this._phaseDefs=Object.entries(t.phases||{}).map(([_,m])=>{let{name:f,checklist:x,consumes_parts:w,required_completion_fields:k,...q}=m,L=m.consumes_parts||[],M=L.findIndex(W=>!W.entry_id),B=M>=0?L[M]:void 0;return{id:_,name:m.name||_,checklistText:(m.checklist||[]).join(`
|
||||
`),partId:B?.part_id||"",partQty:B?.quantity!=null?String(B.quantity):"",reqOverride:m.required_completion_fields!==void 0,reqFields:[...m.required_completion_fields||[]],extraParts:L.filter((W,b)=>b!==M).map(W=>({...W})),carry:q}}),this._phaseSeq=[...t.phase_sequence||[]],this._requiredCompletion=[...t.required_completion_fields||[]],this._scheduleTime=t.schedule_time||"",this._scheduleTimeOn=!!t.schedule_time;let o=t.on_complete_action;if(o&&o.service){this._actionService=o.service;let _=o.target?.entity_id;this._actionTargetEntity=Array.isArray(_)?_[0]||"":_||"",this._actionTargetStored=o.target&&typeof o.target=="object"?JSON.parse(JSON.stringify(o.target)):null,this._actionTargetInitial=this._actionTargetEntity,this._actionData=o.data&&typeof o.data=="object"?{...o.data}:{},this._actionDataJsonFallback="",this._actionSkipAuto=o.skip_auto===!0,this._actionPresent=!0}else this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1;let d=t.quick_complete_defaults;this._qcNotes=d?.notes||"",this._qcCost=d?.cost!=null?String(d.cost):"",this._qcDuration=d?.duration!=null?String(d.duration):"",this._qcFeedback=d?.feedback||"";let p=t.adaptive_config||{};if(this._environmentalEntity=p.environmental_entity||"",this._environmentalAttribute=p.environmental_attribute||"",this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute,this._adaptiveEnabled=!!p.enabled,this._adaptiveAlpha=p.ewa_alpha?.toString()??de.alpha,this._adaptiveMin=p.min_interval_days?.toString()??de.min,this._adaptiveMax=p.max_interval_days?.toString()??de.max,this._adaptiveSeasonal=p.seasonal_enabled!==!1,this._adaptivePrediction=p.sensor_prediction_enabled!==!1,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=this._adaptiveEnabled,t.trigger_config){let _=t.trigger_config;this._triggerEntityId=_.entity_id||_.entity_ids&&_.entity_ids[0]||"",this._triggerEntityIds=_.entity_ids||(_.entity_id?[_.entity_id]:[]),this._triggerEntityLogic=_.entity_logic||"any",this._setTypeFields(cn(_)),this._triggerCombinator=_.trigger_combinator==="all"?"all":"any",this._triggerBaselineValue=_.trigger_baseline_value?.toString()||"",this._liveBaselineValue=t.trigger_baseline_value??null,this._autoCompleteOnRecovery=_.auto_complete_on_recovery||!1,this._triggerRuntimeMaxSession=_.trigger_runtime_max_session_seconds?.toString()||"",_.type==="compound"?(this._compoundLogic=_.compound_logic==="OR"?"OR":"AND",this._compoundConditions=(_.conditions||[]).map(yo)):(this._compoundLogic="AND",this._compoundConditions=[])}else this._resetTriggerFields();this._triggerEntityId&&this._fetchEntityAttributes(this._triggerEntityId),await Promise.all([this._loadUsers(),this._loadTags(),this._loadParts(),this._loadForeignPools()]),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._notifyIcon="",this._priority="normal",this._labels="",this._mirrorTodoEntities=[],this._enabled=!0,this._lastPerformed="",this._nfcTagId="",this._requireTagScan=!1,this._allowSkip=!0,this._readingUnit="",this._readings=[],this._consumesParts={},this._consumesQtyText={},this._responsibleUserId=null,this._assigneePool=[],this._rotationStrategy="",this._checklistText="",this._phaseDefs=[],this._phaseSeq=[],this._requiredCompletion=[],this._scheduleTime="",this._scheduleTimeOn=!1,this._environmentalEntity="",this._environmentalAttribute="",this._environmentalInitial="",this._environmentalAttributeInitial="",this._adaptiveEnabled=!1,this._adaptiveAlpha=de.alpha,this._adaptiveMin=de.min,this._adaptiveMax=de.max,this._adaptiveSeasonal=!0,this._adaptivePrediction=!0,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=!1,this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1,this._actionTesting=!1,this._actionTestResult="",this._qcNotes="",this._qcCost="",this._qcDuration="",this._qcFeedback="",this._resetTriggerFields()}_typeFields(){return{type:this._triggerType,attribute:this._triggerAttribute,above:this._triggerAbove,below:this._triggerBelow,equals:this._triggerEquals,notEquals:this._triggerNotEquals,forMinutes:this._triggerForMinutes,targetValue:this._triggerTargetValue,deltaMode:this._triggerDeltaMode,fromState:this.
Line truncated
|
||||
`),partId:B?.part_id||"",partQty:B?.quantity!=null?String(B.quantity):"",reqOverride:m.required_completion_fields!==void 0,reqFields:[...m.required_completion_fields||[]],extraParts:L.filter((W,b)=>b!==M).map(W=>({...W})),carry:q}}),this._phaseSeq=[...t.phase_sequence||[]],this._requiredCompletion=[...t.required_completion_fields||[]],this._scheduleTime=t.schedule_time||"",this._scheduleTimeOn=!!t.schedule_time;let o=t.on_complete_action;if(o&&o.service){this._actionService=o.service;let _=o.target?.entity_id;this._actionTargetEntity=Array.isArray(_)?_[0]||"":_||"",this._actionTargetStored=o.target&&typeof o.target=="object"?JSON.parse(JSON.stringify(o.target)):null,this._actionTargetInitial=this._actionTargetEntity,this._actionData=o.data&&typeof o.data=="object"?{...o.data}:{},this._actionDataJsonFallback="",this._actionSkipAuto=o.skip_auto===!0,this._actionPresent=!0}else this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1;let d=t.quick_complete_defaults;this._qcNotes=d?.notes||"",this._qcCost=d?.cost!=null?String(d.cost):"",this._qcDuration=d?.duration!=null?String(d.duration):"",this._qcFeedback=d?.feedback||"";let p=t.adaptive_config||{};if(this._environmentalEntity=p.environmental_entity||"",this._environmentalAttribute=p.environmental_attribute||"",this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute,this._adaptiveEnabled=!!p.enabled,this._adaptiveAlpha=p.ewa_alpha?.toString()??de.alpha,this._adaptiveMin=p.min_interval_days?.toString()??de.min,this._adaptiveMax=p.max_interval_days?.toString()??de.max,this._adaptiveSeasonal=p.seasonal_enabled!==!1,this._adaptivePrediction=p.sensor_prediction_enabled!==!1,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=this._adaptiveEnabled,t.trigger_config){let _=t.trigger_config;this._triggerEntityId=an(_)||"",this._triggerEntityIds=ni(_),this._triggerEntityLogic=_.entity_logic||"any",this._setTypeFields(un(_)),this._triggerCombinator=_.trigger_combinator==="all"?"all":"any",this._triggerBaselineValue=_.trigger_baseline_value?.toString()||"",this._liveBaselineValue=t.trigger_baseline_value??null,this._autoCompleteOnRecovery=_.auto_complete_on_recovery||!1,this._triggerRuntimeMaxSession=_.trigger_runtime_max_session_seconds?.toString()||"",_.type==="compound"?(this._compoundLogic=_.compound_logic==="OR"?"OR":"AND",this._compoundConditions=(_.conditions||[]).map(ko)):(this._compoundLogic="AND",this._compoundConditions=[])}else this._resetTriggerFields();this._triggerEntityId&&this._fetchEntityAttributes(this._triggerEntityId),await Promise.all([this._loadUsers(),this._loadTags(),this._loadParts(),this._loadForeignPools()]),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._notifyIcon="",this._priority="normal",this._labels="",this._mirrorTodoEntities=[],this._enabled=!0,this._lastPerformed="",this._nfcTagId="",this._requireTagScan=!1,this._allowSkip=!0,this._readingUnit="",this._readings=[],this._consumesParts={},this._consumesQtyText={},this._responsibleUserId=null,this._assigneePool=[],this._rotationStrategy="",this._checklistText="",this._phaseDefs=[],this._phaseSeq=[],this._requiredCompletion=[],this._scheduleTime="",this._scheduleTimeOn=!1,this._environmentalEntity="",this._environmentalAttribute="",this._environmentalInitial="",this._environmentalAttributeInitial="",this._adaptiveEnabled=!1,this._adaptiveAlpha=de.alpha,this._adaptiveMin=de.min,this._adaptiveMax=de.max,this._adaptiveSeasonal=!0,this._adaptivePrediction=!0,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=!1,this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1,this._actionTesting=!1,this._actionTestResult="",this._qcNotes="",this._qcCost="",this._qcDuration="",this._qcFeedback="",this._resetTriggerFields()}_typeFields(){return{type:this._triggerType,attribute:this._triggerAttribute,above:this._triggerAbove,below:this._triggerBelow,equals:this._triggerEquals,notEquals:this._triggerNotEquals,forMinutes:this._triggerForMinutes,targetValue:this._triggerTargetValue,deltaMode:this._triggerDeltaMode,fromState:this._triggerFromState,toState:this._triggerToState,targetChanges:this._triggerTa
Line truncated
|
||||
<details class="ca-section">
|
||||
<summary>${n("on_complete_action_title",e)}</summary>
|
||||
<p class="field-help">${n("on_complete_action_desc",e)}</p>
|
||||
@@ -1787,7 +1826,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
@input=${d=>{let p=d.target.value;this._consumesQtyText={...this._consumesQtyText,[i]:p};let _=parseFloat(p.replace(",","."));Number.isFinite(_)&&_>=Me[0]&&(this._consumesParts={...this._consumesParts,[i]:{...o,quantity:_}})}}
|
||||
/>`:h}
|
||||
</div>
|
||||
`}_applyConsumesQtyText(){let e={...this._consumesParts};for(let t of Object.keys(e)){let i=this._consumesQtyText[t];if(i===void 0)continue;let a=parseFloat(i.replace(",","."));if(!Number.isFinite(a)||a<Me[0]||a>Me[1]){let o=d=>C(d,this._lang,{maximumFractionDigits:2});return this._error=n("settings_value_out_of_range",this._lang).replace("{min}",o(Me[0])).replace("{max}",o(Me[1])),!1}e[t]={...e[t],quantity:a}}return this._consumesParts=e,!0}_toggleRequired(e,t){let i=new Set(this._requiredCompletion);t?i.add(e):i.delete(e),this._requiredCompletion=[...i]}_patchReading(e,t){this._readings=this._readings.map(i=>i.id===e?{...i,...t}:i)}_renderReadingsEditor(e){let t=Ls(this._readings);return l`
|
||||
`}_applyConsumesQtyText(){let e={...this._consumesParts};for(let t of Object.keys(e)){let i=this._consumesQtyText[t];if(i===void 0)continue;let a=parseFloat(i.replace(",","."));if(!Number.isFinite(a)||a<Me[0]||a>Me[1]){let o=d=>C(d,this._lang,{maximumFractionDigits:2});return this._error=n("settings_value_out_of_range",this._lang).replace("{min}",o(Me[0])).replace("{max}",o(Me[1])),!1}e[t]={...e[t],quantity:a}}return this._consumesParts=e,!0}_toggleRequired(e,t){let i=new Set(this._requiredCompletion);t?i.add(e):i.delete(e),this._requiredCompletion=[...i]}_patchReading(e,t){this._readings=this._readings.map(i=>i.id===e?{...i,...t}:i)}_renderReadingsEditor(e){let t=Ns(this._readings);return l`
|
||||
<div class="readings-editor">
|
||||
<div class="field-label">${n("readings_section",e)}</div>
|
||||
<div class="field-help">${n("readings_hint",e)}</div>
|
||||
@@ -1811,9 +1850,9 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
`)}
|
||||
${this._readings.length<Ui?l`
|
||||
${this._readings.length<Bi?l`
|
||||
<ha-button appearance="plain" class="reading-add"
|
||||
@click=${()=>this._readings=[...this._readings,{id:As(),name:"",unit:this._readings.length?this._readings[this._readings.length-1].unit:this._readingUnit}]}>
|
||||
@click=${()=>this._readings=[...this._readings,{id:Cs(),name:"",unit:this._readings.length?this._readings[this._readings.length-1].unit:this._readingUnit}]}>
|
||||
<ha-icon icon="mdi:plus"></ha-icon> ${n("reading_add",e)}
|
||||
</ha-button>`:h}
|
||||
</div>
|
||||
@@ -1868,7 +1907,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</label>
|
||||
${i.reqOverride?l`
|
||||
<div class="required-completion phase-req-fields">
|
||||
${Yi.map(a=>l`
|
||||
${Qi.map(a=>l`
|
||||
<label class="req-option">
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -1904,8 +1943,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</select>
|
||||
</div>
|
||||
`:h}
|
||||
`}async _save(){if(!this._loading&&this._name.trim()){if(this._adaptiveSnapshot()!==this._adaptiveInitial){let e=parseInt(this._adaptiveMin,10),t=parseInt(this._adaptiveMax,10);if(!isNaN(e)&&!isNaN(t)&&e>t){this._error=`${n("adaptive_min_interval",this._lang)} > ${n("adaptive_max_interval",this._lang)}`;return}}if(this._triggerType==="threshold"&&this._thresholdLimitsOverlap()){this._error=n("trigger_hint_overlap",this._lang);return}if(this._scheduleType==="calendar"&&!this._calendarEntity.trim().startsWith("calendar.")){this._error=n("calendar_entity_required",this._lang);return}if(this._applyConsumesQtyText()){this._loading=!0,this._error="";try{let e=await j(this,()=>this.hass.connection.sendMessagePromise(this._savePayload()),{fallbackKey:"save_error",onError:o=>{this._error=o}});if(e===void 0)return;let t=this._taskId||e?.task_id,i=this._environmentalEntity!==this._environmentalInitial||this._environmentalAttribute!==this._environmentalAttributeInitial;this._warning="";let a=o=>{this._warning=n("subsave_warning",this._lang).replace("{detail}",o)};if(t&&this._scheduleType==="sensor_based"&&i&&await j(this,{type:"maintenance_supporter/task/set_environmental_entity",entry_id:this._entryId,task_id:t,environmental_entity:this._environmentalEntity||null,environmental_attribute:this._environmentalAttribute||null},{onError:a})!==void 0&&(this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute),t&&this._adaptiveSnapshot()!==this._adaptiveInitial){let o=parseFloat(this._adaptiveAlpha),d=parseInt(this._adaptiveMin,10),p=parseInt(this._adaptiveMax,10);await j(this,{type:"maintenance_supporter/task/set_adaptive",entry_id:this._entryId,task_id:t,enabled:this._adaptiveEnabled,...o>=$t[0]&&o<=$t[1]?{ewa_alpha:o}:{},...!isNaN(d)&&d>=1?{min_interval_days:d}:{},...!isNaN(p)&&p>=1?{max_interval_days:p}:{},seasonal_enabled:this._adaptiveSeasonal,sensor_prediction_enabled:this._adaptivePrediction},{onError:a})!==void 0&&(this._adaptiveInitial=this._adaptiveSnapshot())}this._warning?t&&(this._taskId=t):this._open=!1,this.dispatchEvent(new CustomEvent("task-saved"))}finally{this._loading=!1}}}}_savePayload(){let e={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:Number.isNaN(parseInt(this._warningDays,10))?this.defaultWarningDays:Math.max(0,parseInt(this._warningDays,10))},t=this._earliestCompletionDays.trim();e.earliest_completion_days=t===""?null:Math.max(0,parseInt(t,10)||0),this._taskId&&(e.task_id=this._taskId),this._scheduleType==="one_time"?(e.due_date=this._dueDate||null,e.interval_days=null):ni.includes(this._scheduleType)?(e.schedule={...this._buildSchedule(),...this._recurrenceExtras()},e.interval_days=null,this._taskId&&(e.due_date=null)):(this._taskId&&(e.due_date=null),this._scheduleType!=="manual"&&this._intervalDays?(e.interval_days=parseInt(this._intervalDays,10),e.interval_unit=this._intervalUnit,e.interval_anchor=this._intervalAnchor,this._scheduleType==="time_based"&&(e.schedule={kind:"interval",...this._recurrenceExtras()})):this._taskId&&(e.interval_days=null,e.interval_anchor="completion")),e.notes=this._notes||null,e.documentation_url=this._documentationUrl||null,e.custom_icon=this._customIcon||null,e.notify_icon=this._notifyIcon.trim()||null,e.priority=this._priority,e.labels=this._labels.split(",").map(i=>i.trim()).filter(Boolean),e.mirror_todo_entities=this._mirrorTodoEntities.filter(Boolean),e.enabled=this._enabled,(!this._taskId||this._lastPerformed!==this._loadedLastPerformed)&&(e.last_performed=this._lastPerformed||null),e.nfc_tag_id=this._nfcTagId||null,e.require_tag_scan=this._requireTagScan,e.allow_skip=this._allowSkip,e.notify_enabled=this._notifyEnabled,e.reading_unit=this._readingUnit.trim()||null,e.readings=Ns(this._readings);{let i={};for(let o of this._phaseDefs){if(!o.name.trim())continue;let d={...o.carry,name:o.name.trim()},p=o.checklistText.split(`
|
||||
`).map(m=>m.trim()).filter(Boolean);p.length&&(d.checklist=p);let _=[];if(o.partId){let m=parseFloat(o.partQty);_.push({part_id:o.partId,quantity:Number.isFinite(m)&&m>0?m:1})}for(let m of o.extraParts)_.push(m.entry_id?{part_id:m.part_id,quantity:m.quantity,entry_id:m.entry_id}:{part_id:m.part_id,quantity:m.quantity});_.length&&(d.consumes_parts=_),o.reqOverride&&(d.required_completion_fields=[...o.reqFields]),i[o.id]=d}let a=this._phaseSeq.filter(o=>o in i);e.phases=Object.keys(i).length&&a.length?i:null,e.phase_sequence=e.phases?a:null}if((this.parts.length||this._foreignOwners.length)&&(e.consumes_parts=Object.values(this._consumesParts).map(i=>i.entry_id?{part_id:i.part_id,quantity:i.quantity,entry_id:i.entry_id}:{part_id:i.part_id,quantity:i.quantity})),(!this._taskId||this._responsibleUserId!==this._loadedResponsibleUserId)&&(e.responsible_user_id=this._responsibleUserId),e.assignee_pool=this._assigneePool,e.required_completion_fields=this._requiredCompletion,e.rotation_strategy=this._assigneePool.length>=2&&this._rotationStrategy?this._rotationStrategy:null,this._scheduleType==="sensor_based"&&this._triggerType==="compound"){let i=this._compoundConditions.map(bo).filter(a=>a!==null);if(i.length>0){let a={type:"compound",compound_logic:this._compoundLogic,conditions:i};this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),e.trigger_config=a}else this._taskId&&(e.trigger_config=null)}else if(this._scheduleType==="sensor_based"&&this._triggerEntityId){let i=this._triggerEntityIds.length>0?this._triggerEntityIds:[this._triggerEntityId],a={entity_id:i[0],entity_ids:i,type:this._triggerType};if(dn(a,this._typeFields()),this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),i.length>1&&(a.entity_logic=this._triggerEntityLogic),this._triggerType==="counter"&&this._triggerDeltaMode&&this._triggerBaselineValue){let o=parseFloat(this._triggerBaselineValue);!isNaN(o)&&o>=0&&(a.trigger_baseline_value=o)}else if(this._triggerType==="runtime"&&this._triggerRuntimeMaxSession){let o=parseInt(this._triggerRuntimeMaxSession,10);!isNaN(o)&&o>0&&(a.trigger_runtime_max_session_seconds=o)}e.trigger_config=a}else this._taskId&&(e.trigger_config=null);if(this.scheduleTimeEnabled&&nn.includes(this._scheduleType)){let i=this._scheduleTimeOn?this._scheduleTime.trim():"";e.schedule_time=/^([01]\d|2[0-3]):[0-5]\d$/.test(i)?i:null}if(this.checklistsEnabled){let i=this._checklistText.split(`
|
||||
`}async _save(){if(!this._loading&&this._name.trim()){if(this._adaptiveSnapshot()!==this._adaptiveInitial){let e=parseInt(this._adaptiveMin,10),t=parseInt(this._adaptiveMax,10);if(!isNaN(e)&&!isNaN(t)&&e>t){this._error=`${n("adaptive_min_interval",this._lang)} > ${n("adaptive_max_interval",this._lang)}`;return}}if(this._triggerType==="threshold"&&this._thresholdLimitsOverlap()){this._error=n("trigger_hint_overlap",this._lang);return}if(this._scheduleType==="calendar"&&!this._calendarEntity.trim().startsWith("calendar.")){this._error=n("calendar_entity_required",this._lang);return}if(this._applyConsumesQtyText()){this._loading=!0,this._error="";try{let e=await j(this,()=>this.hass.connection.sendMessagePromise(this._savePayload()),{fallbackKey:"save_error",onError:o=>{this._error=o}});if(e===void 0)return;let t=this._taskId||e?.task_id,i=this._environmentalEntity!==this._environmentalInitial||this._environmentalAttribute!==this._environmentalAttributeInitial;this._warning="";let a=o=>{this._warning=n("subsave_warning",this._lang).replace("{detail}",o)};if(t&&this._scheduleType==="sensor_based"&&i&&await j(this,{type:"maintenance_supporter/task/set_environmental_entity",entry_id:this._entryId,task_id:t,environmental_entity:this._environmentalEntity||null,environmental_attribute:this._environmentalAttribute||null},{onError:a})!==void 0&&(this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute),t&&this._adaptiveSnapshot()!==this._adaptiveInitial){let o=parseFloat(this._adaptiveAlpha),d=parseInt(this._adaptiveMin,10),p=parseInt(this._adaptiveMax,10);await j(this,{type:"maintenance_supporter/task/set_adaptive",entry_id:this._entryId,task_id:t,enabled:this._adaptiveEnabled,...o>=$t[0]&&o<=$t[1]?{ewa_alpha:o}:{},...!isNaN(d)&&d>=1?{min_interval_days:d}:{},...!isNaN(p)&&p>=1?{max_interval_days:p}:{},seasonal_enabled:this._adaptiveSeasonal,sensor_prediction_enabled:this._adaptivePrediction},{onError:a})!==void 0&&(this._adaptiveInitial=this._adaptiveSnapshot())}this._warning?t&&(this._taskId=t):this._open=!1,this.dispatchEvent(new CustomEvent("task-saved"))}finally{this._loading=!1}}}}_savePayload(){let e={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:Number.isNaN(parseInt(this._warningDays,10))?this.defaultWarningDays:Math.max(0,parseInt(this._warningDays,10))},t=this._earliestCompletionDays.trim();e.earliest_completion_days=t===""?null:Math.max(0,parseInt(t,10)||0),this._taskId&&(e.task_id=this._taskId),this._scheduleType==="one_time"?(e.due_date=this._dueDate||null,e.interval_days=null):oi.includes(this._scheduleType)?(e.schedule={...this._buildSchedule(),...this._recurrenceExtras()},e.interval_days=null,this._taskId&&(e.due_date=null)):(this._taskId&&(e.due_date=null),this._scheduleType!=="manual"&&this._intervalDays?(e.interval_days=parseInt(this._intervalDays,10),e.interval_unit=this._intervalUnit,e.interval_anchor=this._intervalAnchor,this._scheduleType==="time_based"&&(e.schedule={kind:"interval",...this._recurrenceExtras()})):this._taskId&&(e.interval_days=null,e.interval_anchor="completion")),e.notes=this._notes||null,e.documentation_url=this._documentationUrl||null,e.custom_icon=this._customIcon||null,e.notify_icon=this._notifyIcon.trim()||null,e.priority=this._priority,e.labels=this._labels.split(",").map(i=>i.trim()).filter(Boolean),e.mirror_todo_entities=this._mirrorTodoEntities.filter(Boolean),e.enabled=this._enabled,(!this._taskId||this._lastPerformed!==this._loadedLastPerformed)&&(e.last_performed=this._lastPerformed||null),e.nfc_tag_id=this._nfcTagId||null,e.require_tag_scan=this._requireTagScan,e.allow_skip=this._allowSkip,e.notify_enabled=this._notifyEnabled,e.reading_unit=this._readingUnit.trim()||null,e.readings=qs(this._readings);{let i={};for(let o of this._phaseDefs){if(!o.name.trim())continue;let d={...o.carry,name:o.name.trim()},p=o.checklistText.split(`
|
||||
`).map(m=>m.trim()).filter(Boolean);p.length&&(d.checklist=p);let _=[];if(o.partId){let m=parseFloat(o.partQty);_.push({part_id:o.partId,quantity:Number.isFinite(m)&&m>0?m:1})}for(let m of o.extraParts)_.push(m.entry_id?{part_id:m.part_id,quantity:m.quantity,entry_id:m.entry_id}:{part_id:m.part_id,quantity:m.quantity});_.length&&(d.consumes_parts=_),o.reqOverride&&(d.required_completion_fields=[...o.reqFields]),i[o.id]=d}let a=this._phaseSeq.filter(o=>o in i);e.phases=Object.keys(i).length&&a.length?i:null,e.phase_sequence=e.phases?a:null}if((this.parts.length||this._foreignOwners.length)&&(e.consumes_parts=Object.values(this._consumesParts).map(i=>i.entry_id?{part_id:i.part_id,quantity:i.quantity,entry_id:i.entry_id}:{part_id:i.part_id,quantity:i.quantity})),(!this._taskId||this._responsibleUserId!==this._loadedResponsibleUserId)&&(e.responsible_user_id=this._responsibleUserId),e.assignee_pool=this._assigneePool,e.required_completion_fields=this._requiredCompletion,e.rotation_strategy=this._assigneePool.length>=2&&this._rotationStrategy?this._rotationStrategy:null,this._scheduleType==="sensor_based"&&this._triggerType==="compound"){let i=this._compoundConditions.map($o).filter(a=>a!==null);if(i.length>0){let a={type:"compound",compound_logic:this._compoundLogic,conditions:i};this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),e.trigger_config=a}else this._taskId&&(e.trigger_config=null)}else if(this._scheduleType==="sensor_based"&&this._triggerEntityId){let i=this._triggerEntityIds.length>0?this._triggerEntityIds:[this._triggerEntityId],a={entity_id:i[0],entity_ids:i,type:this._triggerType};if(hn(a,this._typeFields()),this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),i.length>1&&(a.entity_logic=this._triggerEntityLogic),this._triggerType==="counter"&&this._triggerDeltaMode&&this._triggerBaselineValue){let o=parseFloat(this._triggerBaselineValue);!isNaN(o)&&o>=0&&(a.trigger_baseline_value=o)}else if(this._triggerType==="runtime"&&this._triggerRuntimeMaxSession){let o=parseInt(this._triggerRuntimeMaxSession,10);!isNaN(o)&&o>0&&(a.trigger_runtime_max_session_seconds=o)}e.trigger_config=a}else this._taskId&&(e.trigger_config=null);if(this.scheduleTimeEnabled&&ln.includes(this._scheduleType)){let i=this._scheduleTimeOn?this._scheduleTime.trim():"";e.schedule_time=/^([01]\d|2[0-3]):[0-5]\d$/.test(i)?i:null}if(this.checklistsEnabled){let i=this._checklistText.split(`
|
||||
`).map(a=>a.trim()).filter(Boolean).slice(0,100);e.checklist=i.length?i:null}if(this.completionActionsEnabled||this._actionPresent){let i=this._actionService.trim();if(i&&/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(i)){let a={service:i},o=this._actionTargetEntity.trim();this._actionTargetStored&&o===this._actionTargetInitial?a.target=JSON.parse(JSON.stringify(this._actionTargetStored)):o&&(a.target={entity_id:o});let d=this._buildActionData();Object.keys(d).length>0&&(a.data=d),this._actionSkipAuto&&(a.skip_auto=!0),e.on_complete_action=a}else e.on_complete_action=null}if(this.completionActionsEnabled){let i={};this._qcNotes.trim()&&(i.notes=this._qcNotes.trim());let a=parseFloat(this._qcCost);isNaN(a)||(i.cost=a);let o=et(this._qcDuration);o!==null&&(i.duration=o),this._qcFeedback&&(i.feedback=this._qcFeedback),e.quick_complete_defaults=Object.keys(i).length?i:null}return e}_close(){this._open=!1,this._warning="",this._pickerProbeTimer!==void 0&&(clearTimeout(this._pickerProbeTimer),this._pickerProbeTimer=void 0),this._pickerProbeStrikes=0}_renderTriggerFields(){if(this._scheduleType!=="sensor_based")return h;let e=this._lang,t=this._triggerType==="compound";return l`
|
||||
<h3>${n("trigger_configuration",e)}</h3>
|
||||
<div class="select-row">
|
||||
@@ -1914,7 +1953,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._triggerType}
|
||||
@change=${i=>this._triggerType=i.target.value}
|
||||
>
|
||||
${fo.map(i=>l`<option value=${i} ?selected=${i===this._triggerType}>${an(i,e)}</option>`)}
|
||||
${xo.map(i=>l`<option value=${i} ?selected=${i===this._triggerType}>${cn(i,e)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
${t?this._renderCompoundEditor():l`
|
||||
@@ -1928,7 +1967,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ha-form
|
||||
class="entity-picker-form"
|
||||
.hass=${this.hass}
|
||||
.schema=${[{name:"trigger_entities",selector:{entity:{multiple:!0,domain:Ki}}}]}
|
||||
.schema=${[{name:"trigger_entities",selector:{entity:{multiple:!0,domain:Yi}}}]}
|
||||
.data=${{trigger_entities:this._triggerEntityIds.length>0?this._triggerEntityIds:this._triggerEntityId?[this._triggerEntityId]:[]}}
|
||||
.computeLabel=${()=>n("entity_id",e)}
|
||||
@value-changed=${i=>{let a=(i.detail.value.trigger_entities||[]).filter(Boolean);this._triggerEntityId=a[0]||"",this._triggerEntityIds=a,a[0]?this._fetchEntityAttributes(a[0]):this._fetchEntityAttributes("")}}
|
||||
@@ -1976,7 +2015,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</select>
|
||||
</div>
|
||||
`:h}
|
||||
`}_patchCondition(e,t){this._compoundConditions=this._compoundConditions.map((i,a)=>a===e?{...i,...t}:i)}_addCondition(){this._compoundConditions=[...this._compoundConditions,on()]}_removeCondition(e){this._compoundConditions=this._compoundConditions.filter((t,i)=>i!==e)}_renderCompoundEditor(){let e=this._lang;return l`
|
||||
`}_patchCondition(e,t){this._compoundConditions=this._compoundConditions.map((i,a)=>a===e?{...i,...t}:i)}_addCondition(){this._compoundConditions=[...this._compoundConditions,dn()]}_removeCondition(e){this._compoundConditions=this._compoundConditions.filter((t,i)=>i!==e)}_renderCompoundEditor(){let e=this._lang;return l`
|
||||
<div class="select-row">
|
||||
<label>${n("compound_logic",e)}</label>
|
||||
<select
|
||||
@@ -2013,7 +2052,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ha-form
|
||||
class="entity-picker-form"
|
||||
.hass=${this.hass}
|
||||
.schema=${[{name:"condition_entities",selector:{entity:{multiple:!0,domain:Ki}}}]}
|
||||
.schema=${[{name:"condition_entities",selector:{entity:{multiple:!0,domain:Yi}}}]}
|
||||
.data=${{condition_entities:e.entityIds.split(",").map(o=>o.trim()).filter(Boolean)}}
|
||||
.computeLabel=${()=>n("entity_id",i)}
|
||||
@value-changed=${o=>{let d=(o.detail.value.condition_entities||[]).filter(Boolean);this._patchCondition(t,{entityIds:d.join(", ")})}}
|
||||
@@ -2025,7 +2064,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${e.type}
|
||||
@change=${o=>this._patchCondition(t,{type:o.target.value})}
|
||||
>
|
||||
${ln.map(o=>l`<option value=${o} ?selected=${o===e.type}>${an(o,i)}</option>`)}
|
||||
${pn.map(o=>l`<option value=${o} ?selected=${o===e.type}>${cn(o,i)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
${this._renderConditionTypeFields(e,t)}
|
||||
@@ -2175,12 +2214,12 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
>
|
||||
${["days","weeks","months","years"].map(t=>l`<option value=${t} ?selected=${t===this._intervalUnit}>${n("unit_"+t,e)}</option>`)}
|
||||
</select>
|
||||
</div>`}_toggleWeekday(e){this._weekdays=this._weekdays.includes(e)?this._weekdays.filter(t=>t!==e):[...this._weekdays,e]}_previewScheduleDict(){if(this._scheduleType==="one_time")return this._dueDate?{kind:"one_time",due_date:this._dueDate}:null;if(this._scheduleType==="calendar"&&!this._calendarEntity.trim())return null;if(ni.includes(this._scheduleType))return{...this._buildSchedule(),...this._recurrenceExtras()};let e=parseInt(this._intervalDays,10);return this._scheduleType==="manual"||!e||e<=0?null:{kind:"interval",every:e,unit:this._intervalUnit,anchor:this._intervalAnchor,...this._recurrenceExtras()}}updated(e){super.updated?.(e),this._scheduleEntityPickerProbe();for(let t of e.keys())if(g._PREVIEW_RELEVANT.has(String(t))){this._schedulePreviewRefresh();return}}_scheduleEntityPickerProbe(){this._entityPickerFallback||this._pickerProbeTimer!==void 0||!this._open||this._scheduleType!=="sensor_based"||(this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),1500))}_probeEntityPickers(){if(this._pickerProbeTimer=void 0,this._entityPickerFallback||!this._open)return;let e=this.shadowRoot?.querySelector("ha-form.entity-picker-form"),t=(this.shadowRoot?.querySelector(".content")?.offsetHeight??0)>0;if(!e||!t){this._pickerProbeStrikes=0;return}let i=(p,_,m=0)=>{if(!(!p||m>10)){(p.tagName?.toLowerCase()??"")==="ha-entity-picker"&&_.push(p);for(let f of[p.shadowRoot,p])if(f)for(let x of Array.from(f.children??[]))i(x,_,m+1)}},a=[...this.shadowRoot?.querySelectorAll("ha-form.entity-picker-form")??[]],o=[];for(let p of a)i(p,o);let d=o.length===0||o.some(p=>p.offsetHeight===0);if(e.offsetHeight===0||d){if(this._pickerProbeStrikes+=1,this._pickerProbeStrikes>=2){this._entityPickerFallback=!0;return}this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),700)}else this._pickerProbeStrikes=0}_schedulePreviewRefresh(){this._previewTimer&&clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._fetchSchedulePreview()},300)}async _fetchSchedulePreview(){let e=this._open?this._previewScheduleDict():null;if(!e){this._schedulePreview=[],this._schedulePreviewEnded=!1;return}let t=++this._previewSeq;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/schedule/preview",schedule:e,...this._lastPerformed?{last_performed:this._lastPerformed}:{}});if(t!==this._previewSeq)return;this._schedulePreview=i.occurrences||[],this._schedulePreviewEnded=!!i.series_ended}catch{}}_renderSchedulePreview(){if(this._schedulePreview.length===0)return h;let e=this._lang,t=this.scheduleTimeEnabled&&this._scheduleTimeOn&&this._scheduleTime?` ${this._scheduleTime}`:"",i=this._schedulePreview.map((o,d)=>{let p=new Date(`${o}T12:00:00`).getDay();return`${Ke(p===0?6:p-1,e,"short")} ${O(o,e)}${d===0?t:""}`}).join(" \xB7 "),a=this._scheduleType==="time_based"&&this._intervalAnchor==="completion"?l`<div class="field-help">${n("schedule_preview_ontime",e)}</div>`:h;return l`
|
||||
</div>`}_toggleWeekday(e){this._weekdays=this._weekdays.includes(e)?this._weekdays.filter(t=>t!==e):[...this._weekdays,e]}_previewScheduleDict(){if(this._scheduleType==="one_time")return this._dueDate?{kind:"one_time",due_date:this._dueDate}:null;if(this._scheduleType==="calendar"&&!this._calendarEntity.trim())return null;if(oi.includes(this._scheduleType))return{...this._buildSchedule(),...this._recurrenceExtras()};let e=parseInt(this._intervalDays,10);return this._scheduleType==="manual"||!e||e<=0?null:{kind:"interval",every:e,unit:this._intervalUnit,anchor:this._intervalAnchor,...this._recurrenceExtras()}}updated(e){super.updated?.(e),this._scheduleEntityPickerProbe();for(let t of e.keys())if(g._PREVIEW_RELEVANT.has(String(t))){this._schedulePreviewRefresh();return}}_scheduleEntityPickerProbe(){this._entityPickerFallback||this._pickerProbeTimer!==void 0||!this._open||this._scheduleType!=="sensor_based"||(this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),1500))}_probeEntityPickers(){if(this._pickerProbeTimer=void 0,this._entityPickerFallback||!this._open)return;let e=this.shadowRoot?.querySelector("ha-form.entity-picker-form"),t=(this.shadowRoot?.querySelector(".content")?.offsetHeight??0)>0;if(!e||!t){this._pickerProbeStrikes=0;return}let i=(p,_,m=0)=>{if(!(!p||m>10)){(p.tagName?.toLowerCase()??"")==="ha-entity-picker"&&_.push(p);for(let f of[p.shadowRoot,p])if(f)for(let x of Array.from(f.children??[]))i(x,_,m+1)}},a=[...this.shadowRoot?.querySelectorAll("ha-form.entity-picker-form")??[]],o=[];for(let p of a)i(p,o);let d=o.length===0||o.some(p=>p.offsetHeight===0);if(e.offsetHeight===0||d){if(this._pickerProbeStrikes+=1,this._pickerProbeStrikes>=2){this._entityPickerFallback=!0;return}this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),700)}else this._pickerProbeStrikes=0}_schedulePreviewRefresh(){this._previewTimer&&clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._fetchSchedulePreview()},300)}async _fetchSchedulePreview(){let e=this._open?this._previewScheduleDict():null;if(!e){this._schedulePreview=[],this._schedulePreviewEnded=!1;return}let t=++this._previewSeq;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/schedule/preview",schedule:e,...this._lastPerformed?{last_performed:this._lastPerformed}:{}});if(t!==this._previewSeq)return;this._schedulePreview=i.occurrences||[],this._schedulePreviewEnded=!!i.series_ended}catch{}}_renderSchedulePreview(){if(this._schedulePreview.length===0)return h;let e=this._lang,t=this.scheduleTimeEnabled&&this._scheduleTimeOn&&this._scheduleTime?` ${this._scheduleTime}`:"",i=this._schedulePreview.map((o,d)=>{let p=new Date(`${o}T12:00:00`).getDay();return`${Ke(p===0?6:p-1,e,"short")} ${O(o,e)}${d===0?t:""}`}).join(" \xB7 "),a=this._scheduleType==="time_based"&&this._intervalAnchor==="completion"?l`<div class="field-help">${n("schedule_preview_ontime",e)}</div>`:h;return l`
|
||||
<div class="trigger-live-hint schedule-preview">
|
||||
${n("schedule_preview_title",e)}: ${i}${this._schedulePreviewEnded?l` <span class="field-help">${n("schedule_preview_ends",e)}</span>`:h}
|
||||
${a}
|
||||
</div>
|
||||
`}_buildSchedule(){let e=i=>{let a=parseInt(this._calOffset,10)||0;return a&&(i.offset=Math.max(-kt,Math.min(a,kt))),i};if(this._scheduleType==="weekdays")return e({kind:"weekdays",weekdays:[...this._weekdays].sort((i,a)=>i-a)});if(this._scheduleType==="nth_weekday")return e({kind:"nth_weekday",nth:parseInt(this._nth,10),weekday:parseInt(this._nthWeekday,10)});if(this._scheduleType==="calendar")return e({kind:"calendar",entity_id:this._calendarEntity.trim()});let t={kind:"day_of_month",day:this._domLastDay?-1:parseInt(this._domDay,10)||1};return this._domBusiness&&(t.business=!0),e(t)}_recurrenceExtras(){let e={};if(this._seasonMonths.length&&(e.season_months=[...this._seasonMonths].sort((t,i)=>t-i)),this._endsMode==="count"){let t=parseInt(this._endsCount,10);t>=1&&(e.ends={count:t})}else this._endsMode==="until"&&this._endsUntil&&(e.ends={until:this._endsUntil});return e}_toggleSeasonMonth(e){this._seasonMonths=this._seasonMonths.includes(e)?this._seasonMonths.filter(t=>t!==e):[...this._seasonMonths,e]}_renderRecurrenceExtras(){let e=this._lang;if(!(this._scheduleType==="time_based"||ni.includes(this._scheduleType)))return h;let i=wo(e);return l`
|
||||
`}_buildSchedule(){let e=i=>{let a=parseInt(this._calOffset,10)||0;return a&&(i.offset=Math.max(-kt,Math.min(a,kt))),i};if(this._scheduleType==="weekdays")return e({kind:"weekdays",weekdays:[...this._weekdays].sort((i,a)=>i-a)});if(this._scheduleType==="nth_weekday")return e({kind:"nth_weekday",nth:parseInt(this._nth,10),weekday:parseInt(this._nthWeekday,10)});if(this._scheduleType==="calendar")return e({kind:"calendar",entity_id:this._calendarEntity.trim()});let t={kind:"day_of_month",day:this._domLastDay?-1:parseInt(this._domDay,10)||1};return this._domBusiness&&(t.business=!0),e(t)}_recurrenceExtras(){let e={};if(this._seasonMonths.length&&(e.season_months=[...this._seasonMonths].sort((t,i)=>t-i)),this._endsMode==="count"){let t=parseInt(this._endsCount,10);t>=1&&(e.ends={count:t})}else this._endsMode==="until"&&this._endsUntil&&(e.ends={until:this._endsUntil});return e}_toggleSeasonMonth(e){this._seasonMonths=this._seasonMonths.includes(e)?this._seasonMonths.filter(t=>t!==e):[...this._seasonMonths,e]}_renderRecurrenceExtras(){let e=this._lang;if(!(this._scheduleType==="time_based"||oi.includes(this._scheduleType)))return h;let i=So(e);return l`
|
||||
<label class="field-label">${n("season_window_label",e)}</label>
|
||||
<div class="field-help">${n("season_window_hint",e)}</div>
|
||||
<div class="weekday-chips season-chips">
|
||||
@@ -2217,7 +2256,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._endsUntil}
|
||||
@value-changed=${a=>this._endsUntil=a.detail.value}
|
||||
></ms-date-field>`:h}
|
||||
`}_renderCalendarFields(){let e=this._lang,t=xo(e);if(this._scheduleType==="weekdays")return l`
|
||||
`}_renderCalendarFields(){let e=this._lang,t=Eo(e);if(this._scheduleType==="weekdays")return l`
|
||||
<label class="field-label">${n("recurrence_on_days",e)}</label>
|
||||
<div class="weekday-chips">
|
||||
${t.map((i,a)=>l`
|
||||
@@ -2360,7 +2399,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ms-textfield
|
||||
label="${n("target_changes",e)}"
|
||||
type="number"
|
||||
min=${js[0]}
|
||||
min=${Ds[0]}
|
||||
.value=${this._triggerTargetChanges}
|
||||
@input=${t=>this._triggerTargetChanges=t.target.value}
|
||||
></ms-textfield>
|
||||
@@ -2369,7 +2408,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ms-textfield
|
||||
label="${n("for_at_least_minutes",e)}"
|
||||
type="number"
|
||||
min=${Os[0]}
|
||||
min=${js[0]}
|
||||
.value=${this._triggerForMinutes}
|
||||
@input=${t=>this._triggerForMinutes=t.target.value}
|
||||
></ms-textfield>
|
||||
@@ -2432,7 +2471,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._type}
|
||||
@change=${i=>this._type=i.target.value}
|
||||
>
|
||||
${_o.map(i=>l`<option value=${i} ?selected=${i===this._type}>${n(i,e)}</option>`)}
|
||||
${vo.map(i=>l`<option value=${i} ?selected=${i===this._type}>${n(i,e)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
${this._type==="reading"?l`
|
||||
@@ -2467,7 +2506,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._priority}
|
||||
@change=${i=>this._priority=i.target.value}
|
||||
>
|
||||
${mo.map(i=>l`<option value=${i} ?selected=${i===this._priority}>${n("priority_"+i,e)}</option>`)}
|
||||
${yo.map(i=>l`<option value=${i} ?selected=${i===this._priority}>${n("priority_"+i,e)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
@@ -2505,7 +2544,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._scheduleType}
|
||||
@change=${i=>this._scheduleType=i.target.value}
|
||||
>
|
||||
${go.map(i=>l`<option value=${i} ?selected=${i===this._scheduleType}>${n(i,e)}</option>`)}
|
||||
${bo.map(i=>l`<option value=${i} ?selected=${i===this._scheduleType}>${n(i,e)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
${this._scheduleType==="time_based"?l`
|
||||
@@ -2538,7 +2577,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
@value-changed=${i=>this._dueDate=i.detail.value}
|
||||
></ms-date-field>
|
||||
`:h}
|
||||
${this.scheduleTimeEnabled&&nn.includes(this._scheduleType)?l`
|
||||
${this.scheduleTimeEnabled&&ln.includes(this._scheduleType)?l`
|
||||
<label class="checkbox-row schedule-time-toggle">
|
||||
<input type="checkbox" .checked=${this._scheduleTimeOn}
|
||||
@change=${i=>this._scheduleTimeOn=i.target.checked} />
|
||||
@@ -2560,8 +2599,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ms-textfield
|
||||
label="${n("warning_days",e)}"
|
||||
type="number"
|
||||
min=${Vi[0]}
|
||||
max=${Vi[1]}
|
||||
min=${Gi[0]}
|
||||
max=${Gi[1]}
|
||||
.value=${this._warningDays}
|
||||
@input=${i=>this._warningDays=i.target.value}
|
||||
></ms-textfield>
|
||||
@@ -2587,7 +2626,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
${this._renderPhasesEditor(e)}
|
||||
<h3>${n("require_on_completion",e)}</h3>
|
||||
<div class="required-completion">
|
||||
${Yi.map(i=>l`
|
||||
${Qi.map(i=>l`
|
||||
<label class="req-option">
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -2656,7 +2695,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ha-form
|
||||
class="entity-picker-form"
|
||||
.hass=${this.hass}
|
||||
.schema=${[{name:"environmental_entity",selector:{entity:{domain:en,device_class:tn}}}]}
|
||||
.schema=${[{name:"environmental_entity",selector:{entity:{domain:tn,device_class:rn}}}]}
|
||||
.data=${{environmental_entity:this._environmentalEntity}}
|
||||
.computeLabel=${()=>n("environmental_entity_optional",e)}
|
||||
.computeHelper=${()=>n("environmental_entity_helper",e)}
|
||||
@@ -2692,8 +2731,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
@value-changed=${i=>this._notifyIcon=i.detail.value||""}
|
||||
></ha-icon-picker>
|
||||
<div class="field-help notify-icon-help">
|
||||
<ha-icon icon=${this._notifyIcon.trim()||Zi(this._type)}></ha-icon>
|
||||
${n("notify_icon_hint",e).replace("{default}",Zi(this._type))}
|
||||
<ha-icon icon=${this._notifyIcon.trim()||Ji(this._type)}></ha-icon>
|
||||
${n("notify_icon_hint",e).replace("{default}",Ji(this._type))}
|
||||
</div>
|
||||
${this._availableTags.length>0?l`
|
||||
<div class="select-row">
|
||||
@@ -3171,8 +3210,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
`,c([v({attribute:!1})],g.prototype,"hass",2),c([v({type:Boolean,attribute:"checklists-enabled"})],g.prototype,"checklistsEnabled",2),c([v({type:Boolean,attribute:"schedule-time-enabled"})],g.prototype,"scheduleTimeEnabled",2),c([v({type:Boolean,attribute:"completion-actions-enabled"})],g.prototype,"completionActionsEnabled",2),c([v({type:Boolean,attribute:"adaptive-feature"})],g.prototype,"adaptiveFeature",2),c([v({type:Boolean,attribute:"seasonal-feature"})],g.prototype,"seasonalFeature",2),c([v({type:Boolean,attribute:"environmental-feature"})],g.prototype,"environmentalFeature",2),c([v({type:Number,attribute:"default-warning-days"})],g.prototype,"defaultWarningDays",2),c([u()],g.prototype,"parts",2),c([u()],g.prototype,"_foreignOwners",2),c([u()],g.prototype,"_open",2),c([u()],g.prototype,"_entityPickerFallback",2),c([u()],g.prototype,"_loading",2),c([u()],g.prototype,"_error",2),c([u()],g.prototype,"_warning",2),c([u()],g.prototype,"_entryId",2),c([u()],g.prototype,"_taskId",2),c([u()],g.prototype,"_isFleetTask",2),c([u()],g.prototype,"_objectChoices",2),c([u()],g.prototype,"_name",2),c([u()],g.prototype,"_type",2),c([u()],g.prototype,"_scheduleType",2),c([u()],g.prototype,"_intervalDays",2),c([u()],g.prototype,"_intervalUnit",2),c([u()],g.prototype,"_dueDate",2),c([u()],g.prototype,"_warningDays",2),c([u()],g.prototype,"_earliestCompletionDays",2),c([u()],g.prototype,"_intervalAnchor",2),c([u()],g.prototype,"_weekdays",2),c([u()],g.prototype,"_nth",2),c([u()],g.prototype,"_nthWeekday",2),c([u()],g.prototype,"_domDay",2),c([u()],g.prototype,"_domLastDay",2),c([u()],g.prototype,"_domBusiness",2),c([u()],g.prototype,"_calOffset",2),c([u()],g.prototype,"_calendarEntity",2),c([u()],g.prototype,"_seasonMonths",2),c([u()],g.prototype,"_endsMode",2),c([u()],g.prototype,"_endsCount",2),c([u()],g.prototype,"_endsUntil",2),c([u()],g.prototype,"_schedulePreview",2),c([u()],g.prototype,"_schedulePreviewEnded",2),c([u()],g.prototype,"_notes",2),c([u()],g.prototype,"_documentationUrl",2),c([u()],g.prototype,"_customIcon",2),c([u()],g.prototype,"_notifyIcon",2),c([u()],g.prototype,"_priority",2),c([u()],g.prototype,"_labels",2),c([u()],g.prototype,"_mirrorTodoEntities",2),c([u()],g.prototype,"_enabled",2),c([u()],g.prototype,"_triggerEntityId",2),c([u()],g.prototype,"_triggerEntityIds",2),c([u()],g.prototype,"_triggerEntityLogic",2),c([u()],g.prototype,"_triggerAttribute",2),c([u()],g.prototype,"_triggerType",2),c([u()],g.prototype,"_triggerAbove",2),c([u()],g.prototype,"_triggerBelow",2),c([u()],g.prototype,"_triggerEquals",2),c([u()],g.prototype,"_triggerNotEquals",2),c([u()],g.prototype,"_triggerForMinutes",2),c([u()],g.prototype,"_triggerCombinator",2),c([u()],g.prototype,"_triggerTargetValue",2),c([u()],g.prototype,"_triggerDeltaMode",2),c([u()],g.prototype,"_triggerBaselineValue",2),c([u()],g.prototype,"_liveBaselineValue",2),c([u()],g.prototype,"_autoCompleteOnRecovery",2),c([u()],g.prototype,"_triggerFromState",2),c([u()],g.prototype,"_triggerToState",2),c([u()],g.prototype,"_triggerTargetChanges",2),c([u()],g.prototype,"_triggerRuntimeHours",2),c([u()],g.prototype,"_triggerDaysBefore",2),c([u()],g.prototype,"_triggerRuntimeMaxSession",2),c([u()],g.prototype,"_triggerOnStates",2),c([u()],g.prototype,"_compoundLogic",2),c([u()],g.prototype,"_compoundConditions",2),c([u()],g.prototype,"_suggestedAttributes",2),c([u()],g.prototype,"_availableAttributes",2),c([u()],g.prototype,"_entityDomain",2),c([u()],g.prototype,"_lastPerformed",2),c([u()],g.prototype,"_nfcTagId",2),c([u()],g.prototype,"_requireTagScan",2),c([u()],g.prototype,"_allowSkip",2),c([u()],g.prototype,"_notifyEnabled",2),c([u()],g.prototype,"_readingUnit",2),c([u()],g.prototype,"_readings",2),c([u()],g.prototype,"_consumesParts",2),c([u()],g.prototype,"_consumesQtyText",2),c([u()],g.prototype,"_partsLoadFailed",2),c([u()],g.prototype,"_availableTags",2),c([u()],g.prototype,"_responsibleUserId",2),c([u()],g.prototype,"_assigneePool",2),c([u()],g.prototype,"_rotationStrategy",2),c([u()],g.prototype,"_availableUsers",2),c([u()],g.prototype,"_checklistText",2),c([u()],g.prototype,"_phaseDefs",2),c([u()],g.prototype,"_phaseSeq",2),c([u()],g.prototype,"_requiredCompletion",2),c([u()],g.prototype,"_scheduleTime",2),c([u()],g.prototype,"_scheduleTimeOn",2),c([u()],g.prototype,"_actionService",2),c([u()],g.prototype,"_actionTargetEntity",2),c([u()],g.prototype,"_actionSkipAuto",2),c([u()],g.prototype,"_actionPresent",2),c([u()],g.prototype,"_actionData",2),c([u()],g.prototype,"_actionDataJsonFallback",2),c([u()],g.prototype,"_actionTesting",2),c([u()],g.prototype,"_actionTestResult",2),c([u()],g.prototype,"_actionTestError",2),c([u()],g.prototype,"_qcNotes",2),c([u()],g.prototype,"_qcCost",2),c([u()],g.prototype,"_qcDuration",2),c([u()],g.prototype,"_qcFeedback",2),c([u()],g.prototype,"_environmentalEntity",2),c([u()],g.prototype,"_environmentalAttribute",2),c([u()],g.prototype,"_adaptiveEnabled",2),c([u()],g.prototype,"_adaptiveAlpha",2),c([u()],g.prototy
Line truncated
|
||||
`),t=!1;try{navigator.clipboard?.writeText&&(await navigator.clipboard.writeText(e),t=!0)}catch{}if(!t){let i=this.shadowRoot?.querySelector(".details textarea");if(i){i.focus(),i.select();try{t=document.execCommand("copy")}catch{t=!1}}}this._copied=t}async _shoot(){let e=this._video;if(!(!e||this._busy||this._switching)){this._busy=!0;try{let t=e.videoWidth||640,i=e.videoHeight||480,a=Math.min(1,So/Math.max(t,i)),o=document.createElement("canvas");o.width=Math.round(t*a),o.height=Math.round(i*a);let d=o.getContext("2d");if(!d)throw new Error("no_canvas");d.drawImage(e,0,0,o.width,o.height);let p=await new Promise(f=>o.toBlob(f,"image/jpeg",To));if(!p)throw new Error("no_blob");let _=new Date().toISOString().replace(/[-:]/g,"").slice(0,15),m=new File([p],`photo-${_}.jpg`,{type:"image/jpeg"});this.close(),this.dispatchEvent(new CustomEvent("photo-captured",{detail:{file:m},bubbles:!0,composed:!0}))}catch(t){this._unavailable(t instanceof Error?t.message:String(t))}finally{this._busy=!1}}}render(){if(!this._open)return h;let e=this.lang,t=this._busy||this._switching;return l`
|
||||
`,c([v({attribute:!1})],g.prototype,"hass",2),c([v({type:Boolean,attribute:"checklists-enabled"})],g.prototype,"checklistsEnabled",2),c([v({type:Boolean,attribute:"schedule-time-enabled"})],g.prototype,"scheduleTimeEnabled",2),c([v({type:Boolean,attribute:"completion-actions-enabled"})],g.prototype,"completionActionsEnabled",2),c([v({type:Boolean,attribute:"adaptive-feature"})],g.prototype,"adaptiveFeature",2),c([v({type:Boolean,attribute:"seasonal-feature"})],g.prototype,"seasonalFeature",2),c([v({type:Boolean,attribute:"environmental-feature"})],g.prototype,"environmentalFeature",2),c([v({type:Number,attribute:"default-warning-days"})],g.prototype,"defaultWarningDays",2),c([u()],g.prototype,"parts",2),c([u()],g.prototype,"_foreignOwners",2),c([u()],g.prototype,"_open",2),c([u()],g.prototype,"_entityPickerFallback",2),c([u()],g.prototype,"_loading",2),c([u()],g.prototype,"_error",2),c([u()],g.prototype,"_warning",2),c([u()],g.prototype,"_entryId",2),c([u()],g.prototype,"_taskId",2),c([u()],g.prototype,"_isFleetTask",2),c([u()],g.prototype,"_objectChoices",2),c([u()],g.prototype,"_name",2),c([u()],g.prototype,"_type",2),c([u()],g.prototype,"_scheduleType",2),c([u()],g.prototype,"_intervalDays",2),c([u()],g.prototype,"_intervalUnit",2),c([u()],g.prototype,"_dueDate",2),c([u()],g.prototype,"_warningDays",2),c([u()],g.prototype,"_earliestCompletionDays",2),c([u()],g.prototype,"_intervalAnchor",2),c([u()],g.prototype,"_weekdays",2),c([u()],g.prototype,"_nth",2),c([u()],g.prototype,"_nthWeekday",2),c([u()],g.prototype,"_domDay",2),c([u()],g.prototype,"_domLastDay",2),c([u()],g.prototype,"_domBusiness",2),c([u()],g.prototype,"_calOffset",2),c([u()],g.prototype,"_calendarEntity",2),c([u()],g.prototype,"_seasonMonths",2),c([u()],g.prototype,"_endsMode",2),c([u()],g.prototype,"_endsCount",2),c([u()],g.prototype,"_endsUntil",2),c([u()],g.prototype,"_schedulePreview",2),c([u()],g.prototype,"_schedulePreviewEnded",2),c([u()],g.prototype,"_notes",2),c([u()],g.prototype,"_documentationUrl",2),c([u()],g.prototype,"_customIcon",2),c([u()],g.prototype,"_notifyIcon",2),c([u()],g.prototype,"_priority",2),c([u()],g.prototype,"_labels",2),c([u()],g.prototype,"_mirrorTodoEntities",2),c([u()],g.prototype,"_enabled",2),c([u()],g.prototype,"_triggerEntityId",2),c([u()],g.prototype,"_triggerEntityIds",2),c([u()],g.prototype,"_triggerEntityLogic",2),c([u()],g.prototype,"_triggerAttribute",2),c([u()],g.prototype,"_triggerType",2),c([u()],g.prototype,"_triggerAbove",2),c([u()],g.prototype,"_triggerBelow",2),c([u()],g.prototype,"_triggerEquals",2),c([u()],g.prototype,"_triggerNotEquals",2),c([u()],g.prototype,"_triggerForMinutes",2),c([u()],g.prototype,"_triggerCombinator",2),c([u()],g.prototype,"_triggerTargetValue",2),c([u()],g.prototype,"_triggerDeltaMode",2),c([u()],g.prototype,"_triggerBaselineValue",2),c([u()],g.prototype,"_liveBaselineValue",2),c([u()],g.prototype,"_autoCompleteOnRecovery",2),c([u()],g.prototype,"_triggerFromState",2),c([u()],g.prototype,"_triggerToState",2),c([u()],g.prototype,"_triggerTargetChanges",2),c([u()],g.prototype,"_triggerRuntimeHours",2),c([u()],g.prototype,"_triggerDaysBefore",2),c([u()],g.prototype,"_triggerRuntimeMaxSession",2),c([u()],g.prototype,"_triggerOnStates",2),c([u()],g.prototype,"_compoundLogic",2),c([u()],g.prototype,"_compoundConditions",2),c([u()],g.prototype,"_suggestedAttributes",2),c([u()],g.prototype,"_availableAttributes",2),c([u()],g.prototype,"_entityDomain",2),c([u()],g.prototype,"_lastPerformed",2),c([u()],g.prototype,"_nfcTagId",2),c([u()],g.prototype,"_requireTagScan",2),c([u()],g.prototype,"_allowSkip",2),c([u()],g.prototype,"_notifyEnabled",2),c([u()],g.prototype,"_readingUnit",2),c([u()],g.prototype,"_readings",2),c([u()],g.prototype,"_consumesParts",2),c([u()],g.prototype,"_consumesQtyText",2),c([u()],g.prototype,"_partsLoadFailed",2),c([u()],g.prototype,"_availableTags",2),c([u()],g.prototype,"_responsibleUserId",2),c([u()],g.prototype,"_assigneePool",2),c([u()],g.prototype,"_rotationStrategy",2),c([u()],g.prototype,"_availableUsers",2),c([u()],g.prototype,"_checklistText",2),c([u()],g.prototype,"_phaseDefs",2),c([u()],g.prototype,"_phaseSeq",2),c([u()],g.prototype,"_requiredCompletion",2),c([u()],g.prototype,"_scheduleTime",2),c([u()],g.prototype,"_scheduleTimeOn",2),c([u()],g.prototype,"_actionService",2),c([u()],g.prototype,"_actionTargetEntity",2),c([u()],g.prototype,"_actionSkipAuto",2),c([u()],g.prototype,"_actionPresent",2),c([u()],g.prototype,"_actionData",2),c([u()],g.prototype,"_actionDataJsonFallback",2),c([u()],g.prototype,"_actionTesting",2),c([u()],g.prototype,"_actionTestResult",2),c([u()],g.prototype,"_actionTestError",2),c([u()],g.prototype,"_qcNotes",2),c([u()],g.prototype,"_qcCost",2),c([u()],g.prototype,"_qcDuration",2),c([u()],g.prototype,"_qcFeedback",2),c([u()],g.prototype,"_environmentalEntity",2),c([u()],g.prototype,"_environmentalAttribute",2),c([u()],g.prototype,"_adaptiveEnabled",2),c([u()],g.prototype,"_adaptiveAlpha",2),c([u()],g.prototy
Line truncated
|
||||
`),t=!1;try{navigator.clipboard?.writeText&&(await navigator.clipboard.writeText(e),t=!0)}catch{}if(!t){let i=this.shadowRoot?.querySelector(".details textarea");if(i){i.focus(),i.select();try{t=document.execCommand("copy")}catch{t=!1}}}this._copied=t}async _shoot(){let e=this._video;if(!(!e||this._busy||this._switching)){this._busy=!0;try{let t=e.videoWidth||640,i=e.videoHeight||480,a=Math.min(1,Io/Math.max(t,i)),o=document.createElement("canvas");o.width=Math.round(t*a),o.height=Math.round(i*a);let d=o.getContext("2d");if(!d)throw new Error("no_canvas");d.drawImage(e,0,0,o.width,o.height);let p=await new Promise(f=>o.toBlob(f,"image/jpeg",Ro));if(!p)throw new Error("no_blob");let _=new Date().toISOString().replace(/[-:]/g,"").slice(0,15),m=new File([p],`photo-${_}.jpg`,{type:"image/jpeg"});this.close(),this.dispatchEvent(new CustomEvent("photo-captured",{detail:{file:m},bubbles:!0,composed:!0}))}catch(t){this._unavailable(t instanceof Error?t.message:String(t))}finally{this._busy=!1}}}render(){if(!this._open)return h;let e=this.lang,t=this._busy||this._switching;return l`
|
||||
<div class="overlay" role="dialog" aria-modal="true" aria-label=${n("doc_camera",e)}>
|
||||
<video autoplay playsinline muted></video>
|
||||
${this._switching?l`<div class="switching" role="status">${n("camera_switching",e)}</div>`:h}
|
||||
@@ -3237,7 +3276,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.shoot { background: var(--primary-color, #03a9f4); color: #fff; border: none; font-weight: 600; }
|
||||
.shoot[disabled] { opacity: 0.6; cursor: default; }
|
||||
.shoot ha-icon { --mdc-icon-size: 22px; }
|
||||
`,c([v({type:String})],X.prototype,"lang",2),c([u()],X.prototype,"_open",2),c([u()],X.prototype,"_busy",2),c([u()],X.prototype,"_switching",2),c([u()],X.prototype,"_devices",2),c([u()],X.prototype,"_deviceIndex",2),c([u()],X.prototype,"_switchFailed",2),c([u()],X.prototype,"_copied",2);customElements.get("ms-camera-capture")||customElements.define("ms-camera-capture",X)});var oi,ee,At=y(()=>{"use strict";I();F();T();tr();rr();rr();oi=E`
|
||||
`,c([v({type:String})],X.prototype,"lang",2),c([u()],X.prototype,"_open",2),c([u()],X.prototype,"_busy",2),c([u()],X.prototype,"_switching",2),c([u()],X.prototype,"_devices",2),c([u()],X.prototype,"_deviceIndex",2),c([u()],X.prototype,"_switchFailed",2),c([u()],X.prototype,"_copied",2);customElements.get("ms-camera-capture")||customElements.define("ms-camera-capture",X)});var ci,ee,At=y(()=>{"use strict";I();F();T();ir();sr();sr();ci=E`
|
||||
ms-photo-picker { display: contents; }
|
||||
.photo-pickers {
|
||||
display: flex;
|
||||
@@ -3266,7 +3305,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
color: var(--secondary-text-color);
|
||||
margin-top: 4px;
|
||||
}
|
||||
`,ee=class extends S{constructor(){super(...arguments);this.lang="en";this.disabled=!1;this.busy=!1;this.accept="image/*";this.showCamera=!0;this.showGallery=!0;this.compact=!1;this.remaining=1/0;this._singlePick=ai();this._inAppCamera=kn()}createRenderRoot(){return this}get _locked(){return this.disabled||this.busy}_emit(e){e.length!==0&&this.dispatchEvent(new CustomEvent("files-picked",{detail:{files:e},bubbles:!0,composed:!0}))}_onInput(e){let t=e.target,i=Array.from(t.files??[]);t.value="",this._emit(i)}_onCameraClick(e){!this._inAppCamera||this._locked||(e.preventDefault(),this.querySelector("ms-camera-capture")?.open())}_onCameraUnavailable(){this._inAppCamera=!1,this.querySelector(".photo-pick-camera input")?.click()}_onKeydown(e){e.key!=="Enter"&&e.key!==" "||(e.preventDefault(),!this._locked&&e.currentTarget.click())}render(){if(this.remaining<=0||!this.showCamera&&!this.showGallery)return h;let e=this.lang,t=this._locked?"disabled":"",i=this.busy?n("uploading",e):n("doc_camera",e),a=n(this._singlePick?"choose_photo":"choose_photos",e);return l`
|
||||
`,ee=class extends S{constructor(){super(...arguments);this.lang="en";this.disabled=!1;this.busy=!1;this.accept="image/*";this.showCamera=!0;this.showGallery=!0;this.compact=!1;this.remaining=1/0;this._singlePick=li();this._inAppCamera=Sn()}createRenderRoot(){return this}get _locked(){return this.disabled||this.busy}_emit(e){e.length!==0&&this.dispatchEvent(new CustomEvent("files-picked",{detail:{files:e},bubbles:!0,composed:!0}))}_onInput(e){let t=e.target,i=Array.from(t.files??[]);t.value="",this._emit(i)}_onCameraClick(e){!this._inAppCamera||this._locked||(e.preventDefault(),this.querySelector("ms-camera-capture")?.open())}_onCameraUnavailable(){this._inAppCamera=!1,this.querySelector(".photo-pick-camera input")?.click()}_onKeydown(e){e.key!=="Enter"&&e.key!==" "||(e.preventDefault(),!this._locked&&e.currentTarget.click())}render(){if(this.remaining<=0||!this.showCamera&&!this.showGallery)return h;let e=this.lang,t=this._locked?"disabled":"",i=this.busy?n("uploading",e):n("doc_camera",e),a=n(this._singlePick?"choose_photo":"choose_photos",e);return l`
|
||||
<div class="photo-pickers">
|
||||
${this.showCamera?l`<label class="photo-pick photo-pick-camera ${t}" role="button" tabindex="0"
|
||||
aria-label=${i} title=${this.compact?i:h}
|
||||
@@ -3289,7 +3328,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
${this.showCamera&&this._inAppCamera?l`<ms-camera-capture .lang=${e}
|
||||
@photo-captured=${o=>this._emit([o.detail.file])}
|
||||
@capture-unavailable=${this._onCameraUnavailable}></ms-camera-capture>`:h}
|
||||
`}};c([v({type:String})],ee.prototype,"lang",2),c([v({type:Boolean})],ee.prototype,"disabled",2),c([v({type:Boolean})],ee.prototype,"busy",2),c([v({type:String})],ee.prototype,"accept",2),c([v({type:Boolean})],ee.prototype,"showCamera",2),c([v({type:Boolean})],ee.prototype,"showGallery",2),c([v({type:Boolean})],ee.prototype,"compact",2),c([v({type:Number})],ee.prototype,"remaining",2),c([u()],ee.prototype,"_inAppCamera",2);customElements.get("ms-photo-picker")||customElements.define("ms-photo-picker",ee)});var it,$,En=y(()=>{"use strict";I();F();Oe();T();De();Z();Et();ii();Mt();Qi();er();ri();Xe();si();At();At();it=we,$=class extends S{constructor(){super(...arguments);this.entryId="";this.taskId="";this.taskName="";this.lang="en";this.checklist=[];this.adaptiveEnabled=!1;this.taskType="";this.readingUnit="";this.readings=[];this.readingHistory=[];this.restockDefault=null;this.restockUnitCost=null;this.restockPackage="";this.currencySymbol="";this.partsCostMode="purchase";this.parts=[];this.consumesParts=[];this.consumesInfo=[];this.requiredFields=[];this.phaseLabel="";this.requireTagScan=!1;this.viaTagScan=!1;this._open=!1;this._notes="";this._cost="";this._duration="";this._loading=!1;this._error="";this._checklistState={};this._feedback="needed";this._photos=new tt(this,{entryId:()=>this.entryId,hass:()=>this.hass});this._readingValue="";this._readingValues={};this._restockQty="";this._completedAt="";this._usedParts={};this._usedQtyText={};this.checklistPrefill={}}open(e={}){this._open||(this._open=!0,this.viaTagScan=!!e.viaTagScan,this._notes="",this._cost="",this._duration="",this._error="",this._checklistState=Object.fromEntries(this.checklist.map((t,i)=>[String(i),!!this.checklistPrefill[t]]).filter(([,t])=>t)),this._feedback="needed",this._photos.reset(),this._readingValue="",this._readingValues={},this._restockQty=this.restockDefault!==null?String(this.restockDefault):"",this._completedAt="",this._usedParts=Object.fromEntries(this.consumesParts.map(t=>[ae(t),{...t}])),this._usedQtyText={})}_rangeError(e,t){let i=this.lang;return n("settings_value_out_of_range",i).replace("{min}",C(e,i,{maximumFractionDigits:2})).replace("{max}",C(t,i,{maximumFractionDigits:2}))}_toggleCheck(e){let t=String(e);this._checklistState={...this._checklistState,[t]:!this._checklistState[t]}}_setFeedback(e){this._feedback=e}async _complete(){if(this._loading||this._photos.uploading)return;this._error="",this._photos.clearError();let e={type:"maintenance_supporter/task/complete",entry_id:this.entryId,task_id:this.taskId},t=this._notes.trim();if(t&&(e.notes=t),this._cost){let d=parseFloat(this._cost);isNaN(d)||(e.cost=d)}let i=et(this._duration);if(i!==null&&(e.duration=i),this.checklist.length>0&&(e.checklist_state=this._checklistState),this.adaptiveEnabled&&(e.feedback=this._feedback),this._photos.photos.length>0&&(e.photo_doc_ids=this._photos.ids),this.viaTagScan&&(e.via_tag_scan=!0),this._completedAt){if(Q(this._completedAt)>Date.now()){this._error=n("completed_at_future_error",this.lang);return}e.completed_at=this._completedAt.length===16?`${this._completedAt}:00`:this._completedAt}if(this.readings.length>0){let d={};for(let p of this.readings){let _=(this._readingValues[p.id]??"").trim();if(_==="")continue;let m=parseFloat(_.replace(",","."));isNaN(m)||(d[p.id]=m)}Object.keys(d).length>0&&(e.reading_values=d)}else if(this._readingValue!==""){let d=parseFloat(this._readingValue);isNaN(d)||(e.reading_value=d)}if(this.restockDefault!==null&&this._restockQty.trim()!==""){let d=parseFloat(this._restockQty.replace(",","."));if(!Number.isFinite(d)||d<Ze[0]||d>Ze[1]){this._error=this._rangeError(...Ze);return}e.restock_quantity=d}if(this.parts.length>0){let d=[];for(let[p,_]of Object.entries(this._usedParts)){let m=this._usedQtyText[p],f=m===void 0?_.quantity:parseFloat(m.replace(",","."));if(!Number.isFinite(f)||f<it[0]||f>it[1]){this._error=this._rangeError(...it);return}d.push({..._,quantity:f})}e.used_parts=d.map(p=>p.entry_id?{part_id:p.part_id,quantity:p.quantity,entry_id:p.entry_id}:{part_id:p.part_id,quantity:p.quantity})}let a=await j(this,e,{busy:d=>{this._loading=d},lang:this.lang,fallbackKey:"save_error",onError:d=>{this._error=d}});if(a===void 0)return;this._photos.markAttached(),this._open=!1;let o={entryId:this.entryId,taskId:this.taskId,taskName:this.taskName,undo:a?.undo===!0};this.dispatchEvent(new CustomEvent("task-completed",{detail:o}))}_renderReadingField(e,t){let i=this._completedAt?Q(this._completedAt):NaN,a=Rs(this.readingHistory,e.id,isNaN(i)?void 0:i),o=e.unit||this.readingUnit,d=(this._readingValues[e.id]??"").trim(),p=d===""?NaN:parseFloat(d.replace(",",".")),_=a!==void 0&&!isNaN(p)&&p<a.value,m=a!==void 0?C(a.value,t,{maximumFractionDigits:3}):"";return l`
|
||||
`}};c([v({type:String})],ee.prototype,"lang",2),c([v({type:Boolean})],ee.prototype,"disabled",2),c([v({type:Boolean})],ee.prototype,"busy",2),c([v({type:String})],ee.prototype,"accept",2),c([v({type:Boolean})],ee.prototype,"showCamera",2),c([v({type:Boolean})],ee.prototype,"showGallery",2),c([v({type:Boolean})],ee.prototype,"compact",2),c([v({type:Number})],ee.prototype,"remaining",2),c([u()],ee.prototype,"_inAppCamera",2);customElements.get("ms-photo-picker")||customElements.define("ms-photo-picker",ee)});var it,$,An=y(()=>{"use strict";I();F();Oe();T();De();Z();Et();ri();zt();Zi();tr();si();Xe();ai();At();At();it=we,$=class extends S{constructor(){super(...arguments);this.entryId="";this.taskId="";this.taskName="";this.lang="en";this.checklist=[];this.adaptiveEnabled=!1;this.taskType="";this.readingUnit="";this.readings=[];this.readingHistory=[];this.restockDefault=null;this.restockUnitCost=null;this.restockPackage="";this.currencySymbol="";this.partsCostMode="purchase";this.parts=[];this.consumesParts=[];this.consumesInfo=[];this.requiredFields=[];this.phaseLabel="";this.requireTagScan=!1;this.viaTagScan=!1;this._open=!1;this._notes="";this._cost="";this._duration="";this._loading=!1;this._error="";this._checklistState={};this._feedback="needed";this._photos=new tt(this,{entryId:()=>this.entryId,hass:()=>this.hass});this._readingValue="";this._readingValues={};this._restockQty="";this._completedAt="";this._usedParts={};this._usedQtyText={};this.checklistPrefill={}}open(e={}){this._open||(this._open=!0,this.viaTagScan=!!e.viaTagScan,this._notes="",this._cost="",this._duration="",this._error="",this._checklistState=Object.fromEntries(this.checklist.map((t,i)=>[String(i),!!this.checklistPrefill[t]]).filter(([,t])=>t)),this._feedback="needed",this._photos.reset(),this._readingValue="",this._readingValues={},this._restockQty=this.restockDefault!==null?String(this.restockDefault):"",this._completedAt="",this._usedParts=Object.fromEntries(this.consumesParts.map(t=>[ae(t),{...t}])),this._usedQtyText={})}_rangeError(e,t){let i=this.lang;return n("settings_value_out_of_range",i).replace("{min}",C(e,i,{maximumFractionDigits:2})).replace("{max}",C(t,i,{maximumFractionDigits:2}))}_toggleCheck(e){let t=String(e);this._checklistState={...this._checklistState,[t]:!this._checklistState[t]}}_setFeedback(e){this._feedback=e}async _complete(){if(this._loading||this._photos.uploading)return;this._error="",this._photos.clearError();let e={type:"maintenance_supporter/task/complete",entry_id:this.entryId,task_id:this.taskId},t=this._notes.trim();if(t&&(e.notes=t),this._cost){let d=parseFloat(this._cost);isNaN(d)||(e.cost=d)}let i=et(this._duration);if(i!==null&&(e.duration=i),this.checklist.length>0&&(e.checklist_state=this._checklistState),this.adaptiveEnabled&&(e.feedback=this._feedback),this._photos.photos.length>0&&(e.photo_doc_ids=this._photos.ids),this.viaTagScan&&(e.via_tag_scan=!0),this._completedAt){if(Q(this._completedAt)>Date.now()){this._error=n("completed_at_future_error",this.lang);return}e.completed_at=this._completedAt.length===16?`${this._completedAt}:00`:this._completedAt}if(this.readings.length>0){let d={};for(let p of this.readings){let _=(this._readingValues[p.id]??"").trim();if(_==="")continue;let m=parseFloat(_.replace(",","."));isNaN(m)||(d[p.id]=m)}Object.keys(d).length>0&&(e.reading_values=d)}else if(this._readingValue!==""){let d=parseFloat(this._readingValue);isNaN(d)||(e.reading_value=d)}if(this.restockDefault!==null&&this._restockQty.trim()!==""){let d=parseFloat(this._restockQty.replace(",","."));if(!Number.isFinite(d)||d<Ze[0]||d>Ze[1]){this._error=this._rangeError(...Ze);return}e.restock_quantity=d}if(this.parts.length>0){let d=[];for(let[p,_]of Object.entries(this._usedParts)){let m=this._usedQtyText[p],f=m===void 0?_.quantity:parseFloat(m.replace(",","."));if(!Number.isFinite(f)||f<it[0]||f>it[1]){this._error=this._rangeError(...it);return}d.push({..._,quantity:f})}e.used_parts=d.map(p=>p.entry_id?{part_id:p.part_id,quantity:p.quantity,entry_id:p.entry_id}:{part_id:p.part_id,quantity:p.quantity})}let a=await j(this,e,{busy:d=>{this._loading=d},lang:this.lang,fallbackKey:"save_error",onError:d=>{this._error=d}});if(a===void 0)return;this._photos.markAttached(),this._open=!1;let o={entryId:this.entryId,taskId:this.taskId,taskName:this.taskName,undo:a?.undo===!0};this.dispatchEvent(new CustomEvent("task-completed",{detail:o}))}_renderReadingField(e,t){let i=this._completedAt?Q(this._completedAt):NaN,a=Ps(this.readingHistory,e.id,isNaN(i)?void 0:i),o=e.unit||this.readingUnit,d=(this._readingValues[e.id]??"").trim(),p=d===""?NaN:parseFloat(d.replace(",",".")),_=a!==void 0&&!isNaN(p)&&p<a.value,m=a!==void 0?C(a.value,t,{maximumFractionDigits:3}):"";return l`
|
||||
<label class="field reading-field">
|
||||
<span class="field-label">${e.name}${o?` (${o})`:""}</span>
|
||||
<input type="text" inputmode="decimal" class="field-input"
|
||||
@@ -3297,11 +3336,11 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._readingValues[e.id]??""}
|
||||
@input=${f=>{this._readingValues={...this._readingValues,[e.id]:f.target.value}}} />
|
||||
${_?l`<span class="reading-warn">${n("reading_below_last",t).replace("{value}",m)}</span>`:h}
|
||||
</label>`}get _missingRequired(){let e={notes:this._notes.trim()!=="",cost:this._cost.trim()!=="",duration:this._duration.trim()!=="",photo:this._photos.photos.length>0,user:!!this.hass?.user};return this.requiredFields.filter(t=>!e[t])}_demands(e){return this.requiredFields.includes(e)}_req(e){return this._demands(e)?l`<span class="req-mark" aria-hidden="true">*</span>`:h}_purchaseSuggestion(){if(this.restockDefault===null)return null;let e=parseFloat(this._restockQty);return this.restockUnitCost==null||!Number.isFinite(e)||e<=0?null:Math.round(this.restockUnitCost*e*100)/100}_partsValue(){if(this.restockDefault!==null||!this.parts.length)return null;let e=0,t=!1;for(let i of Object.values(this._usedParts)){let a=this.parts.find(d=>ae({part_id:d.id,entry_id:d.entry_id})===ae(i)),o=Vr(a);o!==null&&(e+=o*(i.quantity||1),t=!0)}return t?Math.round(e*100)/100:null}_renderCostSuggestion(e){let t=this._purchaseSuggestion();if(this.restockDefault!==null){let d=this.partsCostMode==="use"?l`<div class="cost-note">${n("cost_purchase_use_hint",e)}</div>`:h;if(this._cost.trim()!==""||t==null||t<=0)return d;let p=J(t,this.currencySymbol,e);return l`<button
|
||||
</label>`}get _missingRequired(){let e={notes:this._notes.trim()!=="",cost:this._cost.trim()!=="",duration:this._duration.trim()!=="",photo:this._photos.photos.length>0,user:!!this.hass?.user};return this.requiredFields.filter(t=>!e[t])}_demands(e){return this.requiredFields.includes(e)}_req(e){return this._demands(e)?l`<span class="req-mark" aria-hidden="true">*</span>`:h}_purchaseSuggestion(){if(this.restockDefault===null)return null;let e=parseFloat(this._restockQty);return this.restockUnitCost==null||!Number.isFinite(e)||e<=0?null:Math.round(this.restockUnitCost*e*100)/100}_partsValue(){if(this.restockDefault!==null||!this.parts.length)return null;let e=0,t=!1;for(let i of Object.values(this._usedParts)){let a=this.parts.find(d=>ae({part_id:d.id,entry_id:d.entry_id})===ae(i)),o=Gr(a);o!==null&&(e+=o*(i.quantity||1),t=!0)}return t?Math.round(e*100)/100:null}_renderCostSuggestion(e){let t=this._purchaseSuggestion();if(this.restockDefault!==null){let d=this.partsCostMode==="use"?l`<div class="cost-note">${n("cost_purchase_use_hint",e)}</div>`:h;if(this._cost.trim()!==""||t==null||t<=0)return d;let p=J(t,this.currencySymbol,e);return l`<button
|
||||
type="button"
|
||||
class="cost-suggestion"
|
||||
@click=${()=>this._cost=String(Math.round(t*100)/100)}
|
||||
>${n("cost_from_parts",e).replace("{amount}",p)}</button>${d}`}let i=this._partsValue();if(i==null||i<=0)return h;let a=J(i,this.currencySymbol,e),o=this.partsCostMode==="use"?"cost_parts_booked":"cost_parts_info";return l`<div class="cost-note">${n(o,e).replace("{amount}",a)}</div>`}_close(){this._open=!1,this._photos.discardOrphans()}_pickCompletedAt(){this._completedAt=`${es()}:00`}render(){if(!this._open)return l``;let e=this.lang||this.hass?.language||"en",t=this._error||this._photos.errorText(e);return l`
|
||||
>${n("cost_from_parts",e).replace("{amount}",p)}</button>${d}`}let i=this._partsValue();if(i==null||i<=0)return h;let a=J(i,this.currencySymbol,e),o=this.partsCostMode==="use"?"cost_parts_booked":"cost_parts_info";return l`<div class="cost-note">${n(o,e).replace("{amount}",a)}</div>`}_close(){this._open=!1,this._photos.discardOrphans()}_pickCompletedAt(){this._completedAt=`${ts()}:00`}render(){if(!this._open)return l``;let e=this.lang||this.hass?.language||"en",t=this._error||this._photos.errorText(e);return l`
|
||||
<ha-dialog open @closed=${this._close}>
|
||||
<div class="dialog-title">${n("complete_title",e)}${this.taskName}</div>
|
||||
${this.phaseLabel?l`<div class="phase-line">${n("phase_current",e)}: ${this.phaseLabel}</div>`:h}
|
||||
@@ -3388,7 +3427,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.hass=${this.hass}
|
||||
.lang=${e}
|
||||
.value=${this._completedAt}
|
||||
.helper=${Yt(e)}
|
||||
.helper=${Qt(e)}
|
||||
@value-changed=${i=>this._completedAt=i.detail.value}
|
||||
></ms-date-field>`:l`<button type="button" class="backdate-pick" @click=${this._pickCompletedAt}>
|
||||
<ha-icon icon="mdi:calendar-clock"></ha-icon>${n("completed_at_pick",e)}
|
||||
@@ -3441,7 +3480,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}};$.styles=[Qe,oi,E`
|
||||
`}};$.styles=[Qe,ci,E`
|
||||
.req-mark {
|
||||
color: var(--error-color, #f44336);
|
||||
margin-left: 2px;
|
||||
@@ -3657,11 +3696,11 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
color: var(--text-primary-color, #fff);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
`],c([v({attribute:!1})],$.prototype,"hass",2),c([v()],$.prototype,"entryId",2),c([v()],$.prototype,"taskId",2),c([v()],$.prototype,"taskName",2),c([v()],$.prototype,"lang",2),c([v({type:Array})],$.prototype,"checklist",2),c([v({type:Boolean})],$.prototype,"adaptiveEnabled",2),c([v()],$.prototype,"taskType",2),c([v()],$.prototype,"readingUnit",2),c([v({attribute:!1})],$.prototype,"readings",2),c([v({attribute:!1})],$.prototype,"readingHistory",2),c([v({attribute:!1})],$.prototype,"restockDefault",2),c([v({attribute:!1})],$.prototype,"restockUnitCost",2),c([v({attribute:!1})],$.prototype,"restockPackage",2),c([v()],$.prototype,"currencySymbol",2),c([v({attribute:!1})],$.prototype,"partsCostMode",2),c([v({attribute:!1})],$.prototype,"parts",2),c([v({attribute:!1})],$.prototype,"consumesParts",2),c([v({type:Array})],$.prototype,"consumesInfo",2),c([v({type:Array})],$.prototype,"requiredFields",2),c([v()],$.prototype,"phaseLabel",2),c([v({type:Boolean})],$.prototype,"requireTagScan",2),c([v({type:Boolean})],$.prototype,"viaTagScan",2),c([u()],$.prototype,"_open",2),c([u()],$.prototype,"_notes",2),c([u()],$.prototype,"_cost",2),c([u()],$.prototype,"_duration",2),c([u()],$.prototype,"_loading",2),c([u()],$.prototype,"_error",2),c([u()],$.prototype,"_checklistState",2),c([u()],$.prototype,"_feedback",2),c([u()],$.prototype,"_readingValue",2),c([u()],$.prototype,"_readingValues",2),c([u()],$.prototype,"_restockQty",2),c([u()],$.prototype,"_completedAt",2),c([u()],$.prototype,"_usedParts",2),c([u()],$.prototype,"_usedQtyText",2),c([v({attribute:!1})],$.prototype,"checklistPrefill",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",$)});function Ro(s,r){s.key!=="Escape"||s.defaultPrevented||s.composedPath().some(e=>e instanceof Element&&e.localName==="ms-camera-capture")||(s.preventDefault(),s.stopPropagation(),r())}function st(s,r){return l`
|
||||
`],c([v({attribute:!1})],$.prototype,"hass",2),c([v()],$.prototype,"entryId",2),c([v()],$.prototype,"taskId",2),c([v()],$.prototype,"taskName",2),c([v()],$.prototype,"lang",2),c([v({type:Array})],$.prototype,"checklist",2),c([v({type:Boolean})],$.prototype,"adaptiveEnabled",2),c([v()],$.prototype,"taskType",2),c([v()],$.prototype,"readingUnit",2),c([v({attribute:!1})],$.prototype,"readings",2),c([v({attribute:!1})],$.prototype,"readingHistory",2),c([v({attribute:!1})],$.prototype,"restockDefault",2),c([v({attribute:!1})],$.prototype,"restockUnitCost",2),c([v({attribute:!1})],$.prototype,"restockPackage",2),c([v()],$.prototype,"currencySymbol",2),c([v({attribute:!1})],$.prototype,"partsCostMode",2),c([v({attribute:!1})],$.prototype,"parts",2),c([v({attribute:!1})],$.prototype,"consumesParts",2),c([v({type:Array})],$.prototype,"consumesInfo",2),c([v({type:Array})],$.prototype,"requiredFields",2),c([v()],$.prototype,"phaseLabel",2),c([v({type:Boolean})],$.prototype,"requireTagScan",2),c([v({type:Boolean})],$.prototype,"viaTagScan",2),c([u()],$.prototype,"_open",2),c([u()],$.prototype,"_notes",2),c([u()],$.prototype,"_cost",2),c([u()],$.prototype,"_duration",2),c([u()],$.prototype,"_loading",2),c([u()],$.prototype,"_error",2),c([u()],$.prototype,"_checklistState",2),c([u()],$.prototype,"_feedback",2),c([u()],$.prototype,"_readingValue",2),c([u()],$.prototype,"_readingValues",2),c([u()],$.prototype,"_restockQty",2),c([u()],$.prototype,"_completedAt",2),c([u()],$.prototype,"_usedParts",2),c([u()],$.prototype,"_usedQtyText",2),c([v({attribute:!1})],$.prototype,"checklistPrefill",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",$)});function qo(s,r){s.key!=="Escape"||s.defaultPrevented||s.composedPath().some(e=>e instanceof Element&&e.localName==="ms-camera-capture")||(s.preventDefault(),s.stopPropagation(),r())}function st(s,r){return l`
|
||||
<div class="backdrop" @click=${s}></div>
|
||||
<div class="dialog" role="dialog" aria-modal="true" tabindex="-1"
|
||||
@keydown=${e=>Ro(e,s)}>${r}</div>
|
||||
`}function nt(s){s?.querySelector(".dialog")?.focus({preventScroll:!0})}var rt,li=y(()=>{"use strict";I();rt=E`
|
||||
@keydown=${e=>qo(e,s)}>${r}</div>
|
||||
`}function nt(s){s?.querySelector(".dialog")?.focus({preventScroll:!0})}var rt,di=y(()=>{"use strict";I();rt=E`
|
||||
.backdrop {
|
||||
position: fixed; inset: 0; z-index: 100;
|
||||
background: rgba(0,0,0,0.5);
|
||||
@@ -3681,7 +3720,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
}
|
||||
/* Focused programmatically on open (so Escape works at once) — no ring. */
|
||||
.dialog:focus { outline: none; }
|
||||
`});var V,Sn=y(()=>{"use strict";I();F();T();V=class extends S{constructor(){super(...arguments);this._open=!1;this._title="";this._message="";this._confirmText="";this._danger=!1;this._inputLabel="";this._inputType="";this._options=null;this._inputValue="";this._resolve=null;this._promptResolve=null}confirm(e){return this._title=e.title,this._message=e.message,this._confirmText=e.confirmText||"OK",this._danger=e.danger||!1,this._inputLabel="",this._inputType="",this._options=null,this._inputValue="",this._open=!0,new Promise(t=>{this._resolve=t,this._promptResolve=null})}prompt(e){return this._title=e.title,this._message=e.message,this._confirmText=e.confirmText||"OK",this._danger=e.danger||!1,this._inputLabel=e.inputLabel||"",this._inputType=e.inputType||"text",this._options=e.options&&e.options.length?e.options:null,this._inputValue=e.inputValue||"",this._open=!0,new Promise(t=>{this._promptResolve=t,this._resolve=null})}_cancel(){this._open=!1,this._promptResolve&&(this._promptResolve({confirmed:!1,value:""}),this._promptResolve=null),this._resolve?.(!1),this._resolve=null}_confirmAction(){this._open=!1,this._promptResolve&&(this._promptResolve({confirmed:!0,value:this._inputValue}),this._promptResolve=null),this._resolve?.(!0),this._resolve=null}render(){if(!this._open)return h;let e=N(this.hass);return l`
|
||||
`});var V,Cn=y(()=>{"use strict";I();F();T();V=class extends S{constructor(){super(...arguments);this._open=!1;this._title="";this._message="";this._confirmText="";this._danger=!1;this._inputLabel="";this._inputType="";this._options=null;this._inputValue="";this._resolve=null;this._promptResolve=null}confirm(e){return this._title=e.title,this._message=e.message,this._confirmText=e.confirmText||"OK",this._danger=e.danger||!1,this._inputLabel="",this._inputType="",this._options=null,this._inputValue="",this._open=!0,new Promise(t=>{this._resolve=t,this._promptResolve=null})}prompt(e){return this._title=e.title,this._message=e.message,this._confirmText=e.confirmText||"OK",this._danger=e.danger||!1,this._inputLabel=e.inputLabel||"",this._inputType=e.inputType||"text",this._options=e.options&&e.options.length?e.options:null,this._inputValue=e.inputValue||"",this._open=!0,new Promise(t=>{this._promptResolve=t,this._resolve=null})}_cancel(){this._open=!1,this._promptResolve&&(this._promptResolve({confirmed:!1,value:""}),this._promptResolve=null),this._resolve?.(!1),this._resolve=null}_confirmAction(){this._open=!1,this._promptResolve&&(this._promptResolve({confirmed:!0,value:this._inputValue}),this._promptResolve=null),this._resolve?.(!0),this._resolve=null}render(){if(!this._open)return h;let e=N(this.hass);return l`
|
||||
<ha-dialog open @closed=${this._cancel}>
|
||||
<div class="dialog-title">${this._title}</div>
|
||||
<div class="content">
|
||||
@@ -3745,7 +3784,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
ha-button.danger {
|
||||
--mdc-theme-primary: var(--error-color, #f44336);
|
||||
}
|
||||
`],c([v({attribute:!1})],V.prototype,"hass",2),c([u()],V.prototype,"_open",2),c([u()],V.prototype,"_title",2),c([u()],V.prototype,"_message",2),c([u()],V.prototype,"_confirmText",2),c([u()],V.prototype,"_danger",2),c([u()],V.prototype,"_inputLabel",2),c([u()],V.prototype,"_inputType",2),c([u()],V.prototype,"_options",2),c([u()],V.prototype,"_inputValue",2);customElements.get("maintenance-confirm-dialog")||customElements.define("maintenance-confirm-dialog",V)});function Po(){return document.querySelector("home-assistant")?.shadowRoot??document.body}function ze(s,r){let e=Po(),t=e.querySelector(`${Tn}[${An}]`);return t||(t=document.createElement(Tn),t.setAttribute(An,""),e.appendChild(t)),t.hass=s,t.confirm(r)}var Tn,An,ci=y(()=>{"use strict";Sn();Tn="maintenance-confirm-dialog",An="data-ms-lovelace-confirm"});function Cn(s,r,e){let t=new Blob([s],{type:e}),i=URL.createObjectURL(t),a=document.createElement("a");a.href=i,a.download=r,a.target="_blank",a.rel="noopener",a.style.display="none",document.body.appendChild(a),a.dispatchEvent(new MouseEvent("click")),document.body.removeChild(a),setTimeout(()=>URL.revokeObjectURL(i),6e4)}var sr=y(()=>{"use strict"});async function Lo(s,r,e=300){return(await s.connection.sendMessagePromise({type:"auth/sign_path",path:r,expires:e})).path}async function In(s,r,e=300){return Lo(s,`/api/maintenance_supporter/document/${r}`,e)}var Rn=y(()=>{"use strict";sr()});var $e,nr=y(()=>{"use strict";I();F();Rn();$e=class extends S{constructor(){super(...arguments);this.docId="";this._url="";this._failed=!1;this._signedFor=""}updated(){this.hass&&this.docId&&this._signedFor!==this.docId&&(this._signedFor=this.docId,this._url="",this._failed=!1,this._sign())}async _sign(){try{this._url=await In(this.hass,this.docId)}catch{this._failed=!0}}render(){return this._failed||!this.docId?h:this._url?l`
|
||||
`],c([v({attribute:!1})],V.prototype,"hass",2),c([u()],V.prototype,"_open",2),c([u()],V.prototype,"_title",2),c([u()],V.prototype,"_message",2),c([u()],V.prototype,"_confirmText",2),c([u()],V.prototype,"_danger",2),c([u()],V.prototype,"_inputLabel",2),c([u()],V.prototype,"_inputType",2),c([u()],V.prototype,"_options",2),c([u()],V.prototype,"_inputValue",2);customElements.get("maintenance-confirm-dialog")||customElements.define("maintenance-confirm-dialog",V)});function Ho(){return document.querySelector("home-assistant")?.shadowRoot??document.body}function ze(s,r){let e=Ho(),t=e.querySelector(`${In}[${Rn}]`);return t||(t=document.createElement(In),t.setAttribute(Rn,""),e.appendChild(t)),t.hass=s,t.confirm(r)}var In,Rn,pi=y(()=>{"use strict";Cn();In="maintenance-confirm-dialog",Rn="data-ms-lovelace-confirm"});function Pn(s,r,e){let t=new Blob([s],{type:e}),i=URL.createObjectURL(t),a=document.createElement("a");a.href=i,a.download=r,a.target="_blank",a.rel="noopener",a.style.display="none",document.body.appendChild(a),a.dispatchEvent(new MouseEvent("click")),document.body.removeChild(a),setTimeout(()=>URL.revokeObjectURL(i),6e4)}var nr=y(()=>{"use strict"});async function Oo(s,r,e=300){return(await s.connection.sendMessagePromise({type:"auth/sign_path",path:r,expires:e})).path}async function Ln(s,r,e=300){return Oo(s,`/api/maintenance_supporter/document/${r}`,e)}var Nn=y(()=>{"use strict";nr()});var $e,ar=y(()=>{"use strict";I();F();Nn();$e=class extends S{constructor(){super(...arguments);this.docId="";this._url="";this._failed=!1;this._signedFor=""}updated(){this.hass&&this.docId&&this._signedFor!==this.docId&&(this._signedFor=this.docId,this._url="",this._failed=!1,this._sign())}async _sign(){try{this._url=await Ln(this.hass,this.docId)}catch{this._failed=!0}}render(){return this._failed||!this.docId?h:this._url?l`
|
||||
<a href=${this._url} target="_blank" rel="noopener" class="wrap">
|
||||
<img src=${this._url} alt="" loading="lazy"
|
||||
@error=${()=>this._failed=!0} />
|
||||
@@ -3769,7 +3808,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
background: var(--secondary-background-color, rgba(0,0,0,0.06));
|
||||
margin-top: 4px;
|
||||
}
|
||||
`,c([v({attribute:!1})],$e.prototype,"hass",2),c([v()],$e.prototype,"docId",2),c([u()],$e.prototype,"_url",2),c([u()],$e.prototype,"_failed",2);customElements.get("maintenance-history-photo")||customElements.define("maintenance-history-photo",$e)});var K,Pn=y(()=>{"use strict";I();F();T();De();Z();Et();li();er();ri();ci();Xe();si();nr();At();At();K=class extends S{constructor(){super(...arguments);this._open=!1;this._saving=!1;this._error="";this._draft=null;this._originalSnapshot=null;this._partOptions=null;this._partQty={};this._partQtyOriginal="";this._openGen=0;this._photos=new tt(this,{entryId:()=>this._draft?.entry_id??"",hass:()=>this.hass});this._photosOriginal="";this._readingRows=[];this._readingText={};this._readingsOriginal=""}get _lang(){return N(this.hass)}openEdit(e){this._openGen++,this._draft={...e},this._originalSnapshot={...e},this._error="",this._open=!0,this._partOptions=null,this._partQty={},this._partQtyOriginal="",this._photos.reset(e.photo_doc_ids??[]),this._photosOriginal=JSON.stringify(this._photos.ids),this._seedReadings(e),this._loadPartOptions()}_seedReadings(e){let t=[],i=new Set;for(let o of e.readings??[])i.has(o.id)||(i.add(o.id),t.push({id:o.id,name:o.name,unit:o.unit??null}));for(let o of e.reading_values??[])i.has(o.id)||(i.add(o.id),t.push({id:o.id,name:o.name,unit:o.unit??null}));this._readingRows=t;let a={};for(let o of e.reading_values??[])a[o.id]=String(o.value);this._readingText=a,this._readingsOriginal=JSON.stringify(this._readingNumbers())}_readingNumbers(){let e={};for(let t of this._readingRows){let i=(this._readingText[t.id]??"").trim();if(i==="")continue;let a=parseFloat(i.replace(",","."));isNaN(a)||(e[t.id]=a)}return e}async _loadPartOptions(){let e=this._draft;if(!e)return;let t=this._openGen;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/parts/overview"});if(t!==this._openGen)return;let a=[];for(let d of i.parts||[]){let p=d.entry_id===e.entry_id,_=d.consumers.some(m=>m.entry_id===e.entry_id&&m.task_id===e.task_id);!p&&!_||a.push({part_id:d.part_id,name:d.name,entry_id:d.entry_id,foreign:!p,object_name:d.object_name})}for(let d of e.used_parts||[]){let p=d.entry_id||e.entry_id;a.some(_=>_.part_id===d.part_id&&_.entry_id===p)||a.push({part_id:d.part_id,name:d.name||d.part_id,entry_id:p,foreign:p!==e.entry_id,object_name:null})}let o={};for(let d of e.used_parts||[])o[`${d.entry_id||e.entry_id}:${d.part_id}`]=d.quantity??1;this._partOptions=a,this._partQty=o,this._partQtyOriginal=this._partSelectionKey()}catch{if(t!==this._openGen)return;this._partOptions=[]}}_partSelectionKey(){return JSON.stringify(Object.entries(this._partQty).filter(([,e])=>e>0).sort(([e],[t])=>e.localeCompare(t)))}close(){this._openGen++,this._open=!1,this._error="",this._draft=null,this._originalSnapshot=null,this._photos.discardOrphans()}_set(e,t){this._draft&&(this._draft={...this._draft,[e]:t})}async _delete(){if(!this._draft||!this._originalSnapshot)return;let e=this._lang,i=(this._originalSnapshot.used_parts||[]).filter(p=>p.quantity>0).map(p=>`${C(p.quantity,e)}\xD7 ${p.name||p.part_id}`).join(", ");if(!await ze(this.hass,{title:n("history_delete_entry",e),message:i?`${n("history_delete_confirm_entry",e)} ${n("history_delete_returns_parts",e).replace("{parts}",i)}`:n("history_delete_confirm_entry",e),confirmText:n("delete",e),danger:!0})||!this._draft||!this._originalSnapshot)return;let o=this._draft;this._error="",await this._runWs({type:"maintenance_supporter/task/history/delete",entry_id:o.entry_id,task_id:o.task_id,timestamp:this._originalSnapshot.original_timestamp})!==void 0&&(this.dispatchEvent(new CustomEvent("history-entry-saved",{detail:{entry_id:o.entry_id,task_id:o.task_id,deleted:!0},bubbles:!0,composed:!0})),this.close())}_runWs(e){return j(this,e,{busy:t=>{this._saving=t},onError:t=>{this._error=t}})}async _save(){if(!this._draft||!this._originalSnapshot||this._saving||this._photos.uploading)return;this._error="",this._photos.clearError();let e=this._draft,t=this._originalSnapshot,i={type:"maintenance_supporter/task/history/update",entry_id:e.entry_id,task_id:e.task_id,original_timestamp:t.original_timestamp};if(e.timestamp!==t.timestamp&&(i.timestamp=e.timestamp),e.notes!==t.notes&&(i.notes=e.notes),e.cost!==t.cost&&(i.cost=e.cost),e.duration!==t.duration&&(i.duration=e.duration),e.completed_by!==t.completed_by&&(i.completed_by=e.completed_by),this._partOptions!==null&&this._partSelectionKey()!==this._partQtyOriginal&&(i.used_parts=(this._partOptions||[]).filter(o=>(this._partQty[`${o.entry_id}:${o.part_id}`]||0)>0).map(o=>({part_id:o.part_id,quantity:this._partQty[`${o.entry_id}:${o.part_id}`],...o.foreign?{entry_id:o.entry_id}:{}}))),e.reading_value!==t.reading_value&&(i.reading_value=e.reading_value??null),this._readingRows.length>0&&JSON.stringify(this._readingNumbers())!==this._readingsOriginal){let o=this._readingNumbers(),d={};for(let p of this._readingRows)d[p.id]=o[p.id]??null;i.reading_values=d}if(JSON.stringify(
Line truncated
|
||||
`,c([v({attribute:!1})],$e.prototype,"hass",2),c([v()],$e.prototype,"docId",2),c([u()],$e.prototype,"_url",2),c([u()],$e.prototype,"_failed",2);customElements.get("maintenance-history-photo")||customElements.define("maintenance-history-photo",$e)});var K,qn=y(()=>{"use strict";I();F();T();De();Z();Et();di();tr();si();pi();Xe();ai();ar();At();At();K=class extends S{constructor(){super(...arguments);this._open=!1;this._saving=!1;this._error="";this._draft=null;this._originalSnapshot=null;this._partOptions=null;this._partQty={};this._partQtyOriginal="";this._openGen=0;this._photos=new tt(this,{entryId:()=>this._draft?.entry_id??"",hass:()=>this.hass});this._photosOriginal="";this._readingRows=[];this._readingText={};this._readingsOriginal=""}get _lang(){return N(this.hass)}openEdit(e){this._openGen++,this._draft={...e},this._originalSnapshot={...e},this._error="",this._open=!0,this._partOptions=null,this._partQty={},this._partQtyOriginal="",this._photos.reset(e.photo_doc_ids??[]),this._photosOriginal=JSON.stringify(this._photos.ids),this._seedReadings(e),this._loadPartOptions()}_seedReadings(e){let t=[],i=new Set;for(let o of e.readings??[])i.has(o.id)||(i.add(o.id),t.push({id:o.id,name:o.name,unit:o.unit??null}));for(let o of e.reading_values??[])i.has(o.id)||(i.add(o.id),t.push({id:o.id,name:o.name,unit:o.unit??null}));this._readingRows=t;let a={};for(let o of e.reading_values??[])a[o.id]=String(o.value);this._readingText=a,this._readingsOriginal=JSON.stringify(this._readingNumbers())}_readingNumbers(){let e={};for(let t of this._readingRows){let i=(this._readingText[t.id]??"").trim();if(i==="")continue;let a=parseFloat(i.replace(",","."));isNaN(a)||(e[t.id]=a)}return e}async _loadPartOptions(){let e=this._draft;if(!e)return;let t=this._openGen;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/parts/overview"});if(t!==this._openGen)return;let a=[];for(let d of i.parts||[]){let p=d.entry_id===e.entry_id,_=d.consumers.some(m=>m.entry_id===e.entry_id&&m.task_id===e.task_id);!p&&!_||a.push({part_id:d.part_id,name:d.name,entry_id:d.entry_id,foreign:!p,object_name:d.object_name})}for(let d of e.used_parts||[]){let p=d.entry_id||e.entry_id;a.some(_=>_.part_id===d.part_id&&_.entry_id===p)||a.push({part_id:d.part_id,name:d.name||d.part_id,entry_id:p,foreign:p!==e.entry_id,object_name:null})}let o={};for(let d of e.used_parts||[])o[`${d.entry_id||e.entry_id}:${d.part_id}`]=d.quantity??1;this._partOptions=a,this._partQty=o,this._partQtyOriginal=this._partSelectionKey()}catch{if(t!==this._openGen)return;this._partOptions=[]}}_partSelectionKey(){return JSON.stringify(Object.entries(this._partQty).filter(([,e])=>e>0).sort(([e],[t])=>e.localeCompare(t)))}close(){this._openGen++,this._open=!1,this._error="",this._draft=null,this._originalSnapshot=null,this._photos.discardOrphans()}_set(e,t){this._draft&&(this._draft={...this._draft,[e]:t})}async _delete(){if(!this._draft||!this._originalSnapshot)return;let e=this._lang,i=(this._originalSnapshot.used_parts||[]).filter(p=>p.quantity>0).map(p=>`${C(p.quantity,e)}\xD7 ${p.name||p.part_id}`).join(", ");if(!await ze(this.hass,{title:n("history_delete_entry",e),message:i?`${n("history_delete_confirm_entry",e)} ${n("history_delete_returns_parts",e).replace("{parts}",i)}`:n("history_delete_confirm_entry",e),confirmText:n("delete",e),danger:!0})||!this._draft||!this._originalSnapshot)return;let o=this._draft;this._error="",await this._runWs({type:"maintenance_supporter/task/history/delete",entry_id:o.entry_id,task_id:o.task_id,timestamp:this._originalSnapshot.original_timestamp})!==void 0&&(this.dispatchEvent(new CustomEvent("history-entry-saved",{detail:{entry_id:o.entry_id,task_id:o.task_id,deleted:!0},bubbles:!0,composed:!0})),this.close())}_runWs(e){return j(this,e,{busy:t=>{this._saving=t},onError:t=>{this._error=t}})}async _save(){if(!this._draft||!this._originalSnapshot||this._saving||this._photos.uploading)return;this._error="",this._photos.clearError();let e=this._draft,t=this._originalSnapshot,i={type:"maintenance_supporter/task/history/update",entry_id:e.entry_id,task_id:e.task_id,original_timestamp:t.original_timestamp};if(e.timestamp!==t.timestamp&&(i.timestamp=e.timestamp),e.notes!==t.notes&&(i.notes=e.notes),e.cost!==t.cost&&(i.cost=e.cost),e.duration!==t.duration&&(i.duration=e.duration),e.completed_by!==t.completed_by&&(i.completed_by=e.completed_by),this._partOptions!==null&&this._partSelectionKey()!==this._partQtyOriginal&&(i.used_parts=(this._partOptions||[]).filter(o=>(this._partQty[`${o.entry_id}:${o.part_id}`]||0)>0).map(o=>({part_id:o.part_id,quantity:this._partQty[`${o.entry_id}:${o.part_id}`],...o.foreign?{entry_id:o.entry_id}:{}}))),e.reading_value!==t.reading_value&&(i.reading_value=e.reading_value??null),this._readingRows.length>0&&JSON.stringify(this._readingNumbers())!==this._readingsOriginal){let o=this._readingNumbers(),d={};for(let p of this._readingRows)d[p.id]=o[p.id]??null;i.reading_values=d}if(JSON.stringify(
Line truncated
|
||||
<h2>${n("history_edit_title",e)}</h2>
|
||||
<div class="entry-type">
|
||||
<ha-icon icon="mdi:tag-outline"></ha-icon>
|
||||
@@ -3781,8 +3820,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.hass=${this.hass}
|
||||
.lang=${e}
|
||||
.label=${n("history_edit_timestamp",e)}
|
||||
.helper=${Yt(e)}
|
||||
.value=${is(t.timestamp)}
|
||||
.helper=${Qt(e)}
|
||||
.value=${rs(t.timestamp)}
|
||||
@value-changed=${a=>{let o=a.detail.value;o&&this._set("timestamp",o)}}
|
||||
></ms-date-field>
|
||||
<label>
|
||||
@@ -3869,7 +3908,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<input type="number" step="any"
|
||||
.value=${e.reading_value!=null?String(e.reading_value):""}
|
||||
@input=${i=>{let a=i.target.value,o=a===""?NaN:Number(a);this._set("reading_value",isNaN(o)?null:o)}} />
|
||||
</label>`}};K.styles=[oi,rt,E`
|
||||
</label>`}};K.styles=[ci,rt,E`
|
||||
:host { display: contents; --ms-modal-max-height: 90vh; --ms-modal-gap: 12px; }
|
||||
h2 { margin: 0; font-size: 18px; }
|
||||
.entry-type {
|
||||
@@ -3959,7 +3998,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
}
|
||||
/* the camera / gallery pickers come from photoPickerStyles (ms-photo-picker) */
|
||||
.photos-hint { font-size: 12px; color: var(--secondary-text-color); }
|
||||
`],c([v({attribute:!1})],K.prototype,"hass",2),c([u()],K.prototype,"_open",2),c([u()],K.prototype,"_saving",2),c([u()],K.prototype,"_error",2),c([u()],K.prototype,"_draft",2),c([u()],K.prototype,"_partOptions",2),c([u()],K.prototype,"_partQty",2),c([u()],K.prototype,"_readingRows",2),c([u()],K.prototype,"_readingText",2);customElements.get("maintenance-history-edit-dialog")||customElements.define("maintenance-history-edit-dialog",K)});function Fe(s){return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function ar(s){return!s.startsWith("data:image/svg+xml,")&&!s.startsWith("data:image/png;base64,")?"":Fe(s)}function No(s){return s.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var Y,Ln=y(()=>{"use strict";I();F();T();sr();Y=class extends S{constructor(){super(...arguments);this.lang="";this._open=!1;this._loading=!1;this._error="";this._viewResult=null;this._completeResult=null;this._quickResult=null;this._urlMode="companion";this._entryId="";this._taskId=null;this._objectName="";this._taskName="";this._generateSeq=0}get _dialogLang(){return this.lang||N(this.hass)}openForObject(e,t){this._entryId=e,this._taskId=null,this._objectName=t,this._taskName="",this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null,this._open=!0,this._generate()}openForTask(e,t,i,a){this._entryId=e,this._taskId=t,this._objectName=i,this._taskName=a,this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null,this._open=!0,this._generate()}async _generate(){let e=++this._generateSeq;this._loading=!0,this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null;try{let t={type:"maintenance_supporter/qr/generate",entry_id:this._entryId,url_mode:this._urlMode};this._taskId&&(t.task_id=this._taskId);let i=[this.hass.connection.sendMessagePromise({...t,action:"view"})];this._taskId&&(i.push(this.hass.connection.sendMessagePromise({...t,action:"complete"})),await this._hasQuickCompleteDefaults()&&i.push(this.hass.connection.sendMessagePromise({...t,action:"quick_complete"})));let a=await Promise.all(i);if(e!==this._generateSeq)return;this._viewResult=a[0],a.length>1&&(this._completeResult=a[1]),a.length>2&&(this._quickResult=a[2])}catch(t){if(e!==this._generateSeq)return;let i=t?.code,a=t?.message;this._error=i==="no_url"||typeof a=="string"&&a.includes("No Home Assistant URL")?n("qr_error_no_url",this._dialogLang):n("qr_error",this._dialogLang)}finally{e===this._generateSeq&&(this._loading=!1)}}async _hasQuickCompleteDefaults(){try{let t=((await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId})).tasks||[]).find(i=>i.id===this._taskId);return!!t?.quick_complete_defaults&&Object.keys(t.quick_complete_defaults).length>0}catch{return!1}}_setUrlMode(e){this._urlMode!==e&&(this._urlMode=e,this._generate())}_print(){if(!this._viewResult)return;let e=this._viewResult,t=e.label.task_name?`${e.label.object_name} \u2014 ${e.label.task_name}`:e.label.object_name,i=[e.label.manufacturer,e.label.model].filter(Boolean).join(" "),a=window.open("","_blank","width=600,height=500");if(!a)return;let o=this._dialogLang,d=Fe(t),p=Fe(i),_=!!this._completeResult,m=!!this._quickResult,f=Fe(n("qr_action_view",o)),x=Fe(n("qr_action_complete",o)),w=Fe(n("qr_action_quick_complete",o));a.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||
`],c([v({attribute:!1})],K.prototype,"hass",2),c([u()],K.prototype,"_open",2),c([u()],K.prototype,"_saving",2),c([u()],K.prototype,"_error",2),c([u()],K.prototype,"_draft",2),c([u()],K.prototype,"_partOptions",2),c([u()],K.prototype,"_partQty",2),c([u()],K.prototype,"_readingRows",2),c([u()],K.prototype,"_readingText",2);customElements.get("maintenance-history-edit-dialog")||customElements.define("maintenance-history-edit-dialog",K)});function Fe(s){return s.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function or(s){return!s.startsWith("data:image/svg+xml,")&&!s.startsWith("data:image/png;base64,")?"":Fe(s)}function jo(s){return s.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var Y,Hn=y(()=>{"use strict";I();F();T();nr();Y=class extends S{constructor(){super(...arguments);this.lang="";this._open=!1;this._loading=!1;this._error="";this._viewResult=null;this._completeResult=null;this._quickResult=null;this._urlMode="companion";this._entryId="";this._taskId=null;this._objectName="";this._taskName="";this._generateSeq=0}get _dialogLang(){return this.lang||N(this.hass)}openForObject(e,t){this._entryId=e,this._taskId=null,this._objectName=t,this._taskName="",this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null,this._open=!0,this._generate()}openForTask(e,t,i,a){this._entryId=e,this._taskId=t,this._objectName=i,this._taskName=a,this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null,this._open=!0,this._generate()}async _generate(){let e=++this._generateSeq;this._loading=!0,this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null;try{let t={type:"maintenance_supporter/qr/generate",entry_id:this._entryId,url_mode:this._urlMode};this._taskId&&(t.task_id=this._taskId);let i=[this.hass.connection.sendMessagePromise({...t,action:"view"})];this._taskId&&(i.push(this.hass.connection.sendMessagePromise({...t,action:"complete"})),await this._hasQuickCompleteDefaults()&&i.push(this.hass.connection.sendMessagePromise({...t,action:"quick_complete"})));let a=await Promise.all(i);if(e!==this._generateSeq)return;this._viewResult=a[0],a.length>1&&(this._completeResult=a[1]),a.length>2&&(this._quickResult=a[2])}catch(t){if(e!==this._generateSeq)return;let i=t?.code,a=t?.message;this._error=i==="no_url"||typeof a=="string"&&a.includes("No Home Assistant URL")?n("qr_error_no_url",this._dialogLang):n("qr_error",this._dialogLang)}finally{e===this._generateSeq&&(this._loading=!1)}}async _hasQuickCompleteDefaults(){try{let t=((await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId})).tasks||[]).find(i=>i.id===this._taskId);return!!t?.quick_complete_defaults&&Object.keys(t.quick_complete_defaults).length>0}catch{return!1}}_setUrlMode(e){this._urlMode!==e&&(this._urlMode=e,this._generate())}_print(){if(!this._viewResult)return;let e=this._viewResult,t=e.label.task_name?`${e.label.object_name} \u2014 ${e.label.task_name}`:e.label.object_name,i=[e.label.manufacturer,e.label.model].filter(Boolean).join(" "),a=window.open("","_blank","width=600,height=500");if(!a)return;let o=this._dialogLang,d=Fe(t),p=Fe(i),_=!!this._completeResult,m=!!this._quickResult,f=Fe(n("qr_action_view",o)),x=Fe(n("qr_action_complete",o)),w=Fe(n("qr_action_quick_complete",o));a.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||
<meta name="color-scheme" content="light">
|
||||
<title>${d}</title>
|
||||
<style>
|
||||
@@ -3981,21 +4020,21 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
${p?`<div class="sub">${p}</div>`:""}
|
||||
<div class="qr-row">
|
||||
<div class="qr-col">
|
||||
<img src="${ar(this._viewResult.svg_data_uri)}" alt="QR Info" />
|
||||
<img src="${or(this._viewResult.svg_data_uri)}" alt="QR Info" />
|
||||
<div class="qr-label">${f}</div>
|
||||
</div>
|
||||
${_?`<div class="qr-col">
|
||||
<img src="${ar(this._completeResult.svg_data_uri)}" alt="QR Complete" />
|
||||
<img src="${or(this._completeResult.svg_data_uri)}" alt="QR Complete" />
|
||||
<div class="qr-label">${x}</div>
|
||||
</div>`:""}
|
||||
${m?`<div class="qr-col">
|
||||
<img src="${ar(this._quickResult.svg_data_uri)}" alt="QR Quick-complete" />
|
||||
<img src="${or(this._quickResult.svg_data_uri)}" alt="QR Quick-complete" />
|
||||
<div class="qr-label">${w}</div>
|
||||
</div>`:""}
|
||||
</div>
|
||||
<div class="url">${Fe(this._viewResult.url)}</div>
|
||||
<script>setTimeout(()=>window.print(),300)<\/script>
|
||||
</body></html>`),a.document.close()}_downloadSvg(e,t){let i=decodeURIComponent(e.svg_data_uri.replace("data:image/svg+xml,","")),a=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;Cn(i,`qr-${No(a)}-${t}.svg`,"image/svg+xml")}_close(){this._open=!1,this._viewResult=null,this._completeResult=null,this._quickResult=null,this._error="",this._loading=!1}render(){if(!this._open)return l``;let e=this._dialogLang,t=this._taskName?`${n("qr_code",e)}: ${this._objectName} \u2014 ${this._taskName}`:`${n("qr_code",e)}: ${this._objectName}`,i=!!this._viewResult;return l`
|
||||
</body></html>`),a.document.close()}_downloadSvg(e,t){let i=decodeURIComponent(e.svg_data_uri.replace("data:image/svg+xml,","")),a=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;Pn(i,`qr-${jo(a)}-${t}.svg`,"image/svg+xml")}_close(){this._open=!1,this._viewResult=null,this._completeResult=null,this._quickResult=null,this._error="",this._loading=!1}render(){if(!this._open)return l``;let e=this._dialogLang,t=this._taskName?`${n("qr_code",e)}: ${this._objectName} \u2014 ${this._taskName}`:`${n("qr_code",e)}: ${this._objectName}`,i=!!this._viewResult;return l`
|
||||
<ha-dialog open @closed=${this._close}>
|
||||
<div class="dialog-title">${t}</div>
|
||||
<div class="content">
|
||||
@@ -4191,9 +4230,9 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
color: var(--text-primary-color, #fff);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
`,c([v({attribute:!1})],Y.prototype,"hass",2),c([v()],Y.prototype,"lang",2),c([u()],Y.prototype,"_open",2),c([u()],Y.prototype,"_loading",2),c([u()],Y.prototype,"_error",2),c([u()],Y.prototype,"_viewResult",2),c([u()],Y.prototype,"_completeResult",2),c([u()],Y.prototype,"_quickResult",2),c([u()],Y.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",Y)});function Nn(s,r,e=new Date){let t=s?.hours,i=Q(s?.snoozed_until);if(typeof t!="number"||!Number.isFinite(i))return n("snoozed",r);let a=fe(),d=ve(i,a)===ve(e,a)?Gt(new Date(i),r):Kt(s.snoozed_until,r);return(t===1?n("snoozed_for_one",r):n("snoozed_for",r).replace("{hours}",C(t,r))).replace("{until}",d)}var qn=y(()=>{"use strict";T();Z()});function qo(s,r){if(r<=0)return 0;let e=typeof s=="number"&&Number.isFinite(s)?Math.trunc(s):0;return e<0?0:e%r}function Ho(s){return!!(s?.phases&&s.phase_sequence&&s.phase_sequence.length>0)}function or(s){if(!s||!Ho(s))return null;let r=s.phase_sequence,e=qo(s.phase_cursor,r.length),t=r[e],i=s.phases?.[t];return i?{id:t,name:i.name,index:e,count:r.length,notes:i.notes,checklist:i.checklist!==void 0?i.checklist:s.checklist??[],consumesParts:i.consumes_parts!==void 0?i.consumes_parts:s.consumes_parts??[],requiredFields:i.required_completion_fields!==void 0?i.required_completion_fields:s.required_completion_fields??[]}:null}function Ct(s){let r=or(s);return r?`${r.index+1}/${r.count} \xB7 ${r.name}`:""}var lr=y(()=>{"use strict"});function Oo(s,r){return typeof s=="number"&&Number.isFinite(s)&&s>=.01?s:r}function Hn(s){let r=s.task??null,e=r?or(r):null,t=e?e.consumesParts:r?.consumes_parts||[],i=!!r?.part_ref,a=s.objects.find(m=>m.entry_id===s.entryId)?.parts||[],o=i?a.find(m=>m.id===r.part_ref.part_id):void 0,d=s.features?s.features.checklists:s.checklistsEnabled??!0,p=s.features?s.features.checklists?s.checklist??r?.checklist??[]:[]:s.checklist??[],_=s.features?s.features.adaptive&&(s.adaptiveEnabled??!!r?.adaptive_config?.enabled):!!s.adaptiveEnabled;return{entry_id:s.entryId,task_id:s.taskId,task_name:s.taskName,checklist:e?d?e.checklist:[]:p,adaptive_enabled:_,required_completion_fields:e?e.requiredFields:r?.required_completion_fields||[],task_type:r?.type||"",reading_unit:r?.reading_unit||"",readings:r?.readings||[],reading_history:Is(r?.history),parts:i?[]:Xs({consumes_parts:t},s.entryId,s.objects,s.lang),consumes_parts:i?[]:t,phase_label:e?Ct(r):"",require_tag_scan:!!r?.require_tag_scan,restock_default:i?Oo(o?.restock_quantity,1):null,restock_unit_cost:i?o?.cost??null:null,restock_package:i&&o?.package_size?Ye(o.package_size,o.unit,s.lang):"",currency_symbol:He({currency_symbol:s.currencySymbol}),consumes_info:t.map(m=>Js(m,s.entryId,s.objects,s.lang)),checklist_prefill:r?.checklist_progress||{},via_tag_scan:!!s.viaTagScan,parts_cost_mode:s.partsCostMode??"purchase"}}function On(s,r,e){s.entryId=r.entry_id,s.taskId=r.task_id,s.taskName=r.task_name,s.lang=e,s.checklist=r.checklist??[],s.adaptiveEnabled=!!r.adaptive_enabled,s.requiredFields=r.required_completion_fields??[],s.taskType=r.task_type??"",s.readingUnit=r.reading_unit??"",s.readings=r.readings??[],s.readingHistory=r.reading_history??[],s.parts=r.parts??[],s.consumesParts=r.consumes_parts??[],s.phaseLabel=r.phase_label??"",s.requireTagScan=!!r.require_tag_scan,s.restockDefault=r.restock_default??null,s.restockUnitCost=r.restock_unit_cost??null,s.restockPackage=r.restock_package??"",s.currencySymbol=He(r),s.consumesInfo=r.consumes_info??[],s.checklistPrefill=r.checklist_prefill??{},s.viaTagScan=!!r.via_tag_scan,s.partsCostMode=r.parts_cost_mode??"purchase",s.open({viaTagScan:!!r.via_tag_scan})}var cr=y(()=>{"use strict";Oe();ii();lr();T()});function di(s){return s?customElements.get("ha-markdown")?l`<ha-markdown class="notes-md" .content=${s} breaks></ha-markdown>`:l`${s}`:h}var dr=y(()=>{"use strict";I()});function jo(s){return s&&jn(s.ref_no)?String(s.ref_no):null}function Dn(s,r){let e=jo(s);return e&&r&&jn(r.ref_no)?`${e}.${r.ref_no}`:null}function Mn(s,r){return s?l`<span class="ref-chip" title=${r??""}>#${s}</span>`:h}var jn,pr=y(()=>{"use strict";I();jn=s=>typeof s=="number"&&Number.isInteger(s)&&s>0});var zn=y(()=>{"use strict"});function Fn(s,r){if(!s)return"";let e=Do[s.trim()];if(e)return n(e,r);for(let[t,i,a]of Mo){let o=s.trim().match(t);if(o){let d=a(o,r);return n(i,r).replace(/\{(\w+)\}/g,(p,_)=>d[_]??p)}}return s}var Do,Mo,Un=y(()=>{"use strict";T();Do={"Completed from dashboard button":"hist_note_button","Completed from the To-do list":"hist_note_todo","Completed by voice":"hist_note_voice","Completed from the notification":"hist_note_notification","Completed via NFC tag":"hist_note_nfc","Completed from the shopping list":"hist_note_shopping_list","Completed from a mirrored to-do list":"hist_note_todo_mirror","Skipped from notification":"hist_note_skipped_notification","Skipped from dashboard button":"hist_note_skipped_button","Sensor trigger activated":"hist_note_sensor_triggereLine truncated
|
||||
`,c([v({attribute:!1})],Y.prototype,"hass",2),c([v()],Y.prototype,"lang",2),c([u()],Y.prototype,"_open",2),c([u()],Y.prototype,"_loading",2),c([u()],Y.prototype,"_error",2),c([u()],Y.prototype,"_viewResult",2),c([u()],Y.prototype,"_completeResult",2),c([u()],Y.prototype,"_quickResult",2),c([u()],Y.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",Y)});function On(s,r,e=new Date){let t=s?.hours,i=Q(s?.snoozed_until);if(typeof t!="number"||!Number.isFinite(i))return n("snoozed",r);let a=fe(),d=ve(i,a)===ve(e,a)?Kt(new Date(i),r):Yt(s.snoozed_until,r);return(t===1?n("snoozed_for_one",r):n("snoozed_for",r).replace("{hours}",C(t,r))).replace("{until}",d)}var jn=y(()=>{"use strict";T();Z()});function Do(s,r){if(r<=0)return 0;let e=typeof s=="number"&&Number.isFinite(s)?Math.trunc(s):0;return e<0?0:e%r}function Mo(s){return!!(s?.phases&&s.phase_sequence&&s.phase_sequence.length>0)}function lr(s){if(!s||!Mo(s))return null;let r=s.phase_sequence,e=Do(s.phase_cursor,r.length),t=r[e],i=s.phases?.[t];return i?{id:t,name:i.name,index:e,count:r.length,notes:i.notes,checklist:i.checklist!==void 0?i.checklist:s.checklist??[],consumesParts:i.consumes_parts!==void 0?i.consumes_parts:s.consumes_parts??[],requiredFields:i.required_completion_fields!==void 0?i.required_completion_fields:s.required_completion_fields??[]}:null}function Ct(s){let r=lr(s);return r?`${r.index+1}/${r.count} \xB7 ${r.name}`:""}var cr=y(()=>{"use strict"});function zo(s,r){return typeof s=="number"&&Number.isFinite(s)&&s>=.01?s:r}function Dn(s){let r=s.task??null,e=r?lr(r):null,t=e?e.consumesParts:r?.consumes_parts||[],i=!!r?.part_ref,a=s.objects.find(m=>m.entry_id===s.entryId)?.parts||[],o=i?a.find(m=>m.id===r.part_ref.part_id):void 0,d=s.features?s.features.checklists:s.checklistsEnabled??!0,p=s.features?s.features.checklists?s.checklist??r?.checklist??[]:[]:s.checklist??[],_=s.features?s.features.adaptive&&(s.adaptiveEnabled??!!r?.adaptive_config?.enabled):!!s.adaptiveEnabled;return{entry_id:s.entryId,task_id:s.taskId,task_name:s.taskName,checklist:e?d?e.checklist:[]:p,adaptive_enabled:_,required_completion_fields:e?e.requiredFields:r?.required_completion_fields||[],task_type:r?.type||"",reading_unit:r?.reading_unit||"",readings:r?.readings||[],reading_history:Rs(r?.history),parts:i?[]:en({consumes_parts:t},s.entryId,s.objects,s.lang),consumes_parts:i?[]:t,phase_label:e?Ct(r):"",require_tag_scan:!!r?.require_tag_scan,restock_default:i?zo(o?.restock_quantity,1):null,restock_unit_cost:i?o?.cost??null:null,restock_package:i&&o?.package_size?Ye(o.package_size,o.unit,s.lang):"",currency_symbol:He({currency_symbol:s.currencySymbol}),consumes_info:t.map(m=>Xs(m,s.entryId,s.objects,s.lang)),checklist_prefill:r?.checklist_progress||{},via_tag_scan:!!s.viaTagScan,parts_cost_mode:s.partsCostMode??"purchase"}}function Mn(s,r,e){s.entryId=r.entry_id,s.taskId=r.task_id,s.taskName=r.task_name,s.lang=e,s.checklist=r.checklist??[],s.adaptiveEnabled=!!r.adaptive_enabled,s.requiredFields=r.required_completion_fields??[],s.taskType=r.task_type??"",s.readingUnit=r.reading_unit??"",s.readings=r.readings??[],s.readingHistory=r.reading_history??[],s.parts=r.parts??[],s.consumesParts=r.consumes_parts??[],s.phaseLabel=r.phase_label??"",s.requireTagScan=!!r.require_tag_scan,s.restockDefault=r.restock_default??null,s.restockUnitCost=r.restock_unit_cost??null,s.restockPackage=r.restock_package??"",s.currencySymbol=He(r),s.consumesInfo=r.consumes_info??[],s.checklistPrefill=r.checklist_prefill??{},s.viaTagScan=!!r.via_tag_scan,s.partsCostMode=r.parts_cost_mode??"purchase",s.open({viaTagScan:!!r.via_tag_scan})}var dr=y(()=>{"use strict";Oe();ri();cr();T()});function ui(s){return s?customElements.get("ha-markdown")?l`<ha-markdown class="notes-md" .content=${s} breaks></ha-markdown>`:l`${s}`:h}var pr=y(()=>{"use strict";I()});function Fo(s){return s&&zn(s.ref_no)?String(s.ref_no):null}function Fn(s,r){let e=Fo(s);return e&&r&&zn(r.ref_no)?`${e}.${r.ref_no}`:null}function Un(s,r){return s?l`<span class="ref-chip" title=${r??""}>#${s}</span>`:h}var zn,ur=y(()=>{"use strict";I();zn=s=>typeof s=="number"&&Number.isInteger(s)&&s>0});var Bn=y(()=>{"use strict"});function Wn(s,r){if(!s)return"";let e=Uo[s.trim()];if(e)return n(e,r);for(let[t,i,a]of Bo){let o=s.trim().match(t);if(o){let d=a(o,r);return n(i,r).replace(/\{(\w+)\}/g,(p,_)=>d[_]??p)}}return s}var Uo,Bo,Vn=y(()=>{"use strict";T();Uo={"Completed from dashboard button":"hist_note_button","Completed from the To-do list":"hist_note_todo","Completed by voice":"hist_note_voice","Completed from the notification":"hist_note_notification","Completed via NFC tag":"hist_note_nfc","Completed from the shopping list":"hist_note_shopping_list","Completed from a mirrored to-do list":"hist_note_todo_mirror","Skipped from notification":"hist_note_skipped_notification","Skipped from dashboard button":"hist_note_skipped_button","Sensor trigger activated":"hist_note_sensor_triggereLine truncated
|
||||
${e.map(t=>l`<maintenance-history-photo .hass=${s} .docId=${t}></maintenance-history-photo>`)}
|
||||
</div>`:h}function Bo(s,r){let e=r.lang,t=o=>C(o,e,{maximumFractionDigits:3}),i=o=>o==null?"":` (${o>=0?"+":""}${t(o)})`,a=be(s);return a.length>0?l`<div class="history-readings">
|
||||
</div>`:h}function Ko(s,r){let e=r.lang,t=o=>C(o,e,{maximumFractionDigits:3}),i=o=>o==null?"":` (${o>=0?"+":""}${t(o)})`,a=be(s);return a.length>0?l`<div class="history-readings">
|
||||
${a.map(o=>l`<span class="history-reading">
|
||||
<span class="history-reading-name">${o.name}</span>
|
||||
<span class="history-reading-value">${t(o.value)}${o.unit?` ${o.unit}`:""}${i(r.readingSlotDelta?.(s,o.id))}</span>
|
||||
@@ -4201,7 +4240,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
</div>`:s.reading_value!=null?l`<div class="history-readings"><span class="history-reading">
|
||||
<span class="history-reading-name">${n("reading_label",e)}</span>
|
||||
<span class="history-reading-value">${t(s.reading_value)}${r.readingUnit?` ${r.readingUnit}`:""}${i(r.readingDelta?.(s))}</span>
|
||||
</span></div>`:h}function Wn(s,r,e={}){let t=r.lang,{compact:i=!1,showRef:a=!0,showBadges:o=!0,showEdit:d=!0}=e,p=r.openEdit,_=d&&!!p&&zo.includes(s.type),m=typeof s.parts_cost=="number"?s.parts_cost:null,f=s.cost_basis==="use",x=f&&!!s.purchase;return l`
|
||||
</span></div>`:h}function Kn(s,r,e={}){let t=r.lang,{compact:i=!1,showRef:a=!0,showBadges:o=!0,showEdit:d=!0}=e,p=r.openEdit,_=d&&!!p&&Wo.includes(s.type),m=typeof s.parts_cost=="number"?s.parts_cost:null,f=s.cost_basis==="use",x=f&&!!s.purchase;return l`
|
||||
<div class="history-entry${i?" compact":""}">
|
||||
${i?h:l`<div class="history-icon ${s.type}">
|
||||
<ha-icon .icon=${vt[s.type]||"mdi:circle"}></ha-icon>
|
||||
@@ -4209,7 +4248,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
<div class="history-content">
|
||||
<div class="history-row">
|
||||
<strong>${n(s.type,t)}</strong>
|
||||
${a?Mn(Fo(r,s),n("ref_number",t)):h}
|
||||
${a?Un(Vo(r,s),n("ref_number",t)):h}
|
||||
${o&&s.phase_id?l`<span class="history-phase-badge">${r.phaseNames?.[s.phase_id]||s.phase_id}</span>`:h}
|
||||
${o&&s.auto?l`<span class="history-auto-badge">${n("history_auto",t)}</span>`:h}
|
||||
${_?l`<button class="history-edit-btn"
|
||||
@@ -4218,35 +4257,35 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
<ha-icon icon="mdi:pencil"></ha-icon>
|
||||
</button>`:h}
|
||||
</div>
|
||||
<div class="history-date">${Kt(s.timestamp,t)}</div>
|
||||
${s.notes?l`<div class="history-notes">${Fn(s.notes,t)}</div>`:h}
|
||||
${Uo(r.hass,s)}
|
||||
${Bo(s,r)}
|
||||
<div class="history-date">${Yt(s.timestamp,t)}</div>
|
||||
${s.notes?l`<div class="history-notes">${Wn(s.notes,t)}</div>`:h}
|
||||
${Go(r.hass,s)}
|
||||
${Ko(s,r)}
|
||||
${s.cost!=null||s.duration!=null||s.trigger_value!=null||m!=null?l`<div class="history-details">
|
||||
${s.cost!=null?x?l`<span>${n("history_purchase_stock",t).replace("{amount}",J(s.cost,r.currencySymbol,t))}</span>`:s.cost<0?l`<span class="history-credit">${n("cost_kind_credit",t)}: ${J(-s.cost,r.currencySymbol,t)}</span>`:l`<span>${n("cost",t)}: ${J(s.cost,r.currencySymbol,t)}</span>`:h}
|
||||
${m!=null?l`<span title=${n(f?"history_parts_counted_hint":"history_parts_info_hint",t)}>${n(f?"history_parts_counted":"history_parts_info",t).replace("{amount}",J(m,r.currencySymbol,t))}</span>`:h}
|
||||
${s.duration!=null?l`<span>${n("duration",t)}: ${Qt(s.duration,t)}</span>`:h}
|
||||
${s.duration!=null?l`<span>${n("duration",t)}: ${Zt(s.duration,t)}</span>`:h}
|
||||
${s.trigger_value!=null?l`<span>${n("trigger_val",t)}: ${s.trigger_value}</span>`:h}
|
||||
</div>`:h}
|
||||
</div>
|
||||
</div>
|
||||
`}var zo,Vn=y(()=>{"use strict";I();T();nr();Xt();pr();Oe();zn();Un();Z();zo=["completed","reset","skipped"]});function It(s){return s.archived?"archived":s.is_done?"done":s.status||"ok"}function ur(s,r){return n(s==="done"?"completed":s,r)}function pi(s){let r=It(s);return r==="done"?Wo:Ci[r]||"var(--disabled-color, #9e9e9e)"}function Vo(s){return vt[s==="done"?"completed":s]||"mdi:circle-medium"}function Gn(s,r,e="pill"){let t=It(s),i=ur(t,r);return e==="chip"?l`<span class="status-chip ${t}">${i}</span>`:l`<span class="status-badge ${t}" role="img" title="${i}" aria-label="${i}"><ha-icon icon="${Vo(t)}"></ha-icon><span class="status-label">${i}</span></span>`}var Wo,hr=y(()=>{"use strict";I();T();zt();Wo="var(--maint-done-color, #78909c)"});var ui,Kn=y(()=>{"use strict";ui=class{constructor(){this._handle=null}schedule(r,e=4e3){this.clear(),this._handle=setTimeout(()=>{this._handle=null,r()},e)}clear(){this._handle!==null&&(clearTimeout(this._handle),this._handle=null)}get pending(){return this._handle!==null}}});function A(s){return s.toFixed(1)}var _r=y(()=>{"use strict";T();Z()});function Yn(s,r){let e=s.interval_analysis,t=e?.weibull_beta,i=e?.weibull_eta;if(t==null||i==null||i<=0)return h;let a=s.interval_days??0,o=s.suggested_interval??a;return l`
|
||||
`}var Wo,Yn=y(()=>{"use strict";I();T();ar();ei();ur();Oe();Bn();Vn();Z();Wo=["completed","reset","skipped"]});function It(s){return s.archived?"archived":s.is_done?"done":s.status||"ok"}function hr(s,r){return n(s==="done"?"completed":s,r)}function hi(s){let r=It(s);return r==="done"?Yo:Ii[r]||"var(--disabled-color, #9e9e9e)"}function Qo(s){return vt[s==="done"?"completed":s]||"mdi:circle-medium"}function Qn(s,r,e="pill"){let t=It(s),i=hr(t,r);return e==="chip"?l`<span class="status-chip ${t}">${i}</span>`:l`<span class="status-badge ${t}" role="img" title="${i}" aria-label="${i}"><ha-icon icon="${Qo(t)}"></ha-icon><span class="status-label">${i}</span></span>`}var Yo,_r=y(()=>{"use strict";I();T();Ft();Yo="var(--maint-done-color, #78909c)"});var _i,Zn=y(()=>{"use strict";_i=class{constructor(){this._handle=null}schedule(r,e=4e3){this.clear(),this._handle=setTimeout(()=>{this._handle=null,r()},e)}clear(){this._handle!==null&&(clearTimeout(this._handle),this._handle=null)}get pending(){return this._handle!==null}}});function A(s){return s.toFixed(1)}var mr=y(()=>{"use strict";T();Z()});function Jn(s,r){let e=s.interval_analysis,t=e?.weibull_beta,i=e?.weibull_eta;if(t==null||i==null||i<=0)return h;let a=s.interval_days??0,o=s.suggested_interval??a;return l`
|
||||
<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>
|
||||
${n("weibull_reliability_curve",r)}
|
||||
${Go(t,r)}
|
||||
${Zo(t,r)}
|
||||
</div>
|
||||
${Ko(t,i,a,o,r)}
|
||||
${Yo(e,r)}
|
||||
${e?.confidence_interval_low!=null?Qo(e,s,r):h}
|
||||
${Jo(t,i,a,o,r)}
|
||||
${Xo(e,r)}
|
||||
${e?.confidence_interval_low!=null?el(e,s,r):h}
|
||||
</div>
|
||||
`}function Go(s,r){let e,t,i;return s<.8?(e="early_failures",t="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z",i="beta_early_failures"):s<=1.2?(e="random_failures",t="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"):s<=3.5?(e="wear_out",t="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"):(e="highly_predictable",t="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"),l`
|
||||
`}function Zo(s,r){let e,t,i;return s<.8?(e="early_failures",t="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z",i="beta_early_failures"):s<=1.2?(e="random_failures",t="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"):s<=3.5?(e="wear_out",t="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"):(e="highly_predictable",t="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"),l`
|
||||
<span class="beta-badge ${e}">
|
||||
<ha-svg-icon path="${t}"></ha-svg-icon>
|
||||
${n(i,r)} (\u03B2=${C(s,r,2)})
|
||||
</span>
|
||||
`}function Ko(s,r,e,t,i){let w=Math.max(e,t,r,1)*1.3,k=50,q=[];for(let H=0;H<=k;H++){let z=H/k*w,ot=1-Math.exp(-Math.pow(z/r,s)),lt=32+z/w*260,_i=136-ot*128;q.push([lt,_i])}let L=q.map(([H,z])=>`${A(H)},${A(z)}`).join(" "),M="M32,136 "+q.map(([H,z])=>`L${A(H)},${A(z)}`).join(" ")+` L${A(q[k][0])},136 Z`,B=32+e/w*260,W=1-Math.exp(-Math.pow(e/r,s)),b=136-W*128,le=C((1-W)*100,i,0),Ee=32+t/w*260,_e=[0,.25,.5,.75,1];return l`
|
||||
`}function Jo(s,r,e,t,i){let w=Math.max(e,t,r,1)*1.3,k=50,q=[];for(let H=0;H<=k;H++){let z=H/k*w,ot=1-Math.exp(-Math.pow(z/r,s)),lt=32+z/w*260,mi=136-ot*128;q.push([lt,mi])}let L=q.map(([H,z])=>`${A(H)},${A(z)}`).join(" "),M="M32,136 "+q.map(([H,z])=>`L${A(H)},${A(z)}`).join(" ")+` L${A(q[k][0])},136 Z`,B=32+e/w*260,W=1-Math.exp(-Math.pow(e/r,s)),b=136-W*128,le=C((1-W)*100,i,0),Ee=32+t/w*260,_e=[0,.25,.5,.75,1];return l`
|
||||
<div class="weibull-chart">
|
||||
<svg viewBox="0 0 ${300} ${160}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${n("chart_weibull",i)}">
|
||||
${_e.map(H=>{let z=136-H*128;return We`
|
||||
@@ -4289,7 +4328,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
${e>0?l`<span class="legend-item"><span class="legend-swatch" style="background:var(--primary-color, #03a9f4); opacity:0.5"></span> ${n("current_interval_marker",i)}</span>`:h}
|
||||
${t>0&&t!==e?l`<span class="legend-item"><span class="legend-swatch" style="background:var(--success-color, #4caf50)"></span> ${n("recommended_marker",i)}</span>`:h}
|
||||
</div>
|
||||
`}function Yo(s,r){return l`
|
||||
`}function Xo(s,r){return l`
|
||||
<div class="weibull-info-row">
|
||||
<div class="weibull-info-item">
|
||||
<span>${n("characteristic_life",r)}</span>
|
||||
@@ -4302,7 +4341,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
</div>
|
||||
`:h}
|
||||
</div>
|
||||
`}function Qo(s,r,e){let t=s.confidence_interval_low,i=s.confidence_interval_high,a=r.suggested_interval??r.interval_days??0,o=r.interval_days??0,d=Math.max(0,t-5),_=i+5-d,m=(t-d)/_*100,f=(i-t)/_*100,x=(a-d)/_*100,w=o>0?(o-d)/_*100:-1;return l`
|
||||
`}function el(s,r,e){let t=s.confidence_interval_low,i=s.confidence_interval_high,a=r.suggested_interval??r.interval_days??0,o=r.interval_days??0,d=Math.max(0,t-5),_=i+5-d,m=(t-d)/_*100,f=(i-t)/_*100,x=(a-d)/_*100,w=o>0?(o-d)/_*100:-1;return l`
|
||||
<div class="confidence-range">
|
||||
<div class="confidence-range-title">
|
||||
${n("confidence_interval",e)}: ${a} ${n("days",e)} (${t}\u2013${i})
|
||||
@@ -4317,7 +4356,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
<span class="confidence-text high">${n("confidence_aggressive",e)} (${i}${n("days",e).charAt(0)})</span>
|
||||
</div>
|
||||
</div>
|
||||
`}var Qn=y(()=>{"use strict";I();T();_r()});function Zn(s,r,e){let t=s.degradation_trend!=null&&s.degradation_trend!=="insufficient_data",i=s.days_until_threshold!=null,a=s.environmental_factor!=null&&s.environmental_factor!==1;if(!t&&!i&&!a)return h;let o=s.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":s.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 l`
|
||||
`}var Xn=y(()=>{"use strict";I();T();mr()});function ea(s,r,e){let t=s.degradation_trend!=null&&s.degradation_trend!=="insufficient_data",i=s.days_until_threshold!=null,a=s.environmental_factor!=null&&s.environmental_factor!==1;if(!t&&!i&&!a)return h;let o=s.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":s.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 l`
|
||||
<div class="prediction-section">
|
||||
${s.sensor_prediction_urgency?l`
|
||||
<div class="prediction-urgency-banner">
|
||||
@@ -4353,12 +4392,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">${n("environmental_adjustment",r)}</span>
|
||||
<span class="prediction-value">${C(s.environmental_factor,r,2)}x</span>
|
||||
${s.environmental_entity?l`<span class="prediction-entity entity-link" @click=${d=>Es(d,s.environmental_entity)}>${s.environmental_entity}</span>`:h}
|
||||
${s.environmental_entity?l`<span class="prediction-entity entity-link" @click=${d=>Ss(d,s.environmental_entity)}>${s.environmental_entity}</span>`:h}
|
||||
</div>
|
||||
`:h}
|
||||
</div>
|
||||
</div>
|
||||
`}var Jn=y(()=>{"use strict";I();T()});function Xn(s,r,e,t){let i=Math.max(s||1,r);return l`
|
||||
`}var ta=y(()=>{"use strict";I();T()});function ia(s,r,e,t){let i=Math.max(s||1,r);return l`
|
||||
<div class="interval-comparison">
|
||||
<div class="interval-bar">
|
||||
<div class="interval-label">
|
||||
@@ -4376,7 +4415,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
style="width: ${Math.min(r/i*100,100)}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
`}var ea=y(()=>{"use strict";I();T()});function ia(){return Number(Ie().slice(5,7))-1}function ra(s,r,e){if(!e.seasonal||!s.seasonal_factor||s.seasonal_factor===1)return h;let t=ta.map(d=>n(d,r)),i=ia(),a=s.seasonal_factors||s.interval_analysis?.seasonal_factors||null,o=a&&a.length===12?a:t.map((d,p)=>{let _=s.seasonal_factor||1,m=Math.sin((p-6)*Math.PI/6)*.3;return Math.max(.7,Math.min(1.3,_+m))});return l`
|
||||
`}var ra=y(()=>{"use strict";I();T()});function na(){return Number(Ie().slice(5,7))-1}function aa(s,r,e){if(!e.seasonal||!s.seasonal_factor||s.seasonal_factor===1)return h;let t=sa.map(d=>n(d,r)),i=na(),a=s.seasonal_factors||s.interval_analysis?.seasonal_factors||null,o=a&&a.length===12?a:t.map((d,p)=>{let _=s.seasonal_factor||1,m=Math.sin((p-6)*Math.PI/6)*.3;return Math.max(.7,Math.min(1.3,_+m))});return l`
|
||||
<div class="seasonal-card-compact">
|
||||
<h4>${n("seasonal_awareness",r)}</h4>
|
||||
<div class="seasonal-mini-chart">
|
||||
@@ -4393,7 +4432,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
<span class="legend-item"><span class="dot high"></span> ${n("longer",r)}</span>
|
||||
</div>
|
||||
</div>
|
||||
`}function sa(s,r){return Zo(s,r)}function Zo(s,r){let e=s.seasonal_factors??s.interval_analysis?.seasonal_factors;if(!e||e.length!==12)return h;let t=s.interval_analysis?.seasonal_reason,i=ia(),a=300,o=100,d=8,_=o-d-4,m=Math.max(...e,1.5),f=a/12,x=f*.65,w=d+_-1/m*_;return l`
|
||||
`}function oa(s,r){return tl(s,r)}function tl(s,r){let e=s.seasonal_factors??s.interval_analysis?.seasonal_factors;if(!e||e.length!==12)return h;let t=s.interval_analysis?.seasonal_reason,i=na(),a=300,o=100,d=8,_=o-d-4,m=Math.max(...e,1.5),f=a/12,x=f*.65,w=d+_-1/m*_;return l`
|
||||
<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>
|
||||
@@ -4410,13 +4449,13 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
`})}
|
||||
</svg>
|
||||
<div class="seasonal-labels">
|
||||
${ta.map((k,q)=>l`<span class="seasonal-label ${q===i?"active-month":""}">${n(k,r)}</span>`)}
|
||||
${sa.map((k,q)=>l`<span class="seasonal-label ${q===i?"active-month":""}">${n(k,r)}</span>`)}
|
||||
</div>
|
||||
</div>
|
||||
`}var ta,na=y(()=>{"use strict";I();T();_r();Z();ta=["month_jan","month_feb","month_mar","month_apr","month_may","month_jun","month_jul","month_aug","month_sep","month_oct","month_nov","month_dec"]});var R,aa=y(()=>{"use strict";I();F();T();De();qn();li();Z();cr();lr();dr();Wi();Oe();pr();Vn();hr();St();xt();ci();Kn();Qn();Jn();ea();Xe();na();R=class extends S{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._taskId=null;this._task=null;this._objectName="";this._taskRef=null;this._busy=!1;this._error="";this._showSkip=!1;this._showReset=!1;this._showPostpone=!1;this._showDetails=!1;this._showAdaptive=!1;this._skipReason="";this._resetDate="";this._postponeDate="";this._features={adaptive:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1};this._toast="";this._toastTimer=new ui;this._access=je;this._currencySymbol="";this._partsCostMode="purchase";this._loadSeq=0}get _lang(){return N(this.hass)}async openFor(e,t){(e!==this._entryId||t!==this._taskId)&&(this._task=null),this._entryId=e,this._taskId=t,this._error="",this._showSkip=!1,this._showReset=!1,this._showPostpone=!1,this._showAdaptive=!1,this._skipReason="",this._postponeDate="",this._resetDate=Ie(),this._open=!0,await Promise.all([this._loadTask(),this._loadFeatures()])}async _loadFeatures(){let e=await ne(this.hass);this._features={...this._features,...e.features},this._access=e.access,this._currencySymbol=He(e.budget??void 0),Vt(e.budget??void 0),this._partsCostMode=e.partsCostMode}close(){this._loadSeq++,this._open=!1,this._task=null,this._error="",this._toastTimer.clear(),this._toast=""}_showToast(e,t){this._toast=e,this._toastTimer.schedule(()=>{this._toast=""},t)}async _loadTask(){let e=this._entryId,t=this._taskId;if(!e||!t)return;let i=++this._loadSeq,a="",o=await j(this,{type:"maintenance_supporter/object",entry_id:e},{onError:p=>{a=p}});if(i!==this._loadSeq)return;if(o===void 0){this._error=a;return}this._objectName=o?.object?.name||"";let d=(o?.tasks||[]).find(p=>p.id===t);this._task=d??null,this._taskRef=Dn(o?.object,d),d||(this._error=n("ws_err_not_found",this._lang))}_runWs(e){return this._error="",j(this,e,{busy:t=>{this._busy=t},onError:t=>{this._error=t}})}_notifyChanged(e){this.dispatchEvent(new CustomEvent("task-action-fired",{detail:{entry_id:this._entryId,task_id:this._taskId,action:e},bubbles:!0,composed:!0}))}_onComplete(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(oe(),pe)).then(async({openCompleteDialog:e})=>{let t=this._task,i=[];try{i=(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects",compact:!0})).objects||[]}catch{}e(Hn({entryId:this._entryId,taskId:this._taskId,taskName:t.name,task:t,objects:i,lang:this._lang,features:this._features,currencySymbol:this._currencySymbol,partsCostMode:this._partsCostMode}))&&(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})!==void 0&&(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})!==void 0&&(this._notifyChanged("reset"),this.close())}async _onPostponeConfirm(){if(!this._entryId||!this._taskId||!this._postponeDate)return;await this._runWs({type:"maintenance_supporter/task/postpone",entry_id:this._entryId,task_id:this._taskId,until:this._postponeDate})!==void 0&&(this._notifyChanged("postpone"),this.close())}async _onSnooze(){if(!this._entryId||!this._taskId)return;let e=await this._runWs({type:"maintenance_supporter/task/snooze",entry_id:this._entryId,task_id:this._taskId});e!==void 0&&(this._notifyChanged("snooze"),this.dispatchEvent(new CustomEvent("hass-notification",{detail:{message:Nn(e,this._lang)},bubbles:!0,composed:!0})),this.close())}_onEdit(){!this._entryId||!this._taskId||Promise.resolve().then(()=>(oe(),pe)).then(({openEditTaskDialog:e})=>{e(this._entryId,this._taskId),this.close()})}_onQr(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(oe(),pe)).then(({openQrDialog:e})=>{e({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||!await ze(this.hass,{title:n("delete",this._lang),message:n("delete_task_confirm",this._lang),confirmText:n("delete",this._lang),danger:!0}))return;await this._runWs({type:"maintenance_supporter/task/delete",entry_id:this._entryId,task_id:this._taskId})!==void 0&&(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})!==void 0&&(this._not
Line truncated
|
||||
`}var sa,la=y(()=>{"use strict";I();T();mr();Z();sa=["month_jan","month_feb","month_mar","month_apr","month_may","month_jun","month_jul","month_aug","month_sep","month_oct","month_nov","month_dec"]});var P,ca=y(()=>{"use strict";I();F();T();De();jn();di();Z();dr();cr();pr();Vi();Oe();ur();Yn();_r();St();xt();pi();Zn();Xn();ta();ra();Xe();la();P=class extends S{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._taskId=null;this._task=null;this._objectName="";this._taskRef=null;this._busy=!1;this._error="";this._showSkip=!1;this._showReset=!1;this._showPostpone=!1;this._showDetails=!1;this._showAdaptive=!1;this._skipReason="";this._resetDate="";this._postponeDate="";this._features={adaptive:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1,places:!1};this._toast="";this._toastTimer=new _i;this._access=je;this._currencySymbol="";this._partsCostMode="purchase";this._loadSeq=0}get _lang(){return N(this.hass)}async openFor(e,t){(e!==this._entryId||t!==this._taskId)&&(this._task=null),this._entryId=e,this._taskId=t,this._error="",this._showSkip=!1,this._showReset=!1,this._showPostpone=!1,this._showAdaptive=!1,this._skipReason="",this._postponeDate="",this._resetDate=Ie(),this._open=!0,await Promise.all([this._loadTask(),this._loadFeatures()])}async _loadFeatures(){let e=await se(this.hass);this._features={...this._features,...e.features},this._access=e.access,this._currencySymbol=He(e.budget??void 0),Gt(e.budget??void 0),this._partsCostMode=e.partsCostMode}close(){this._loadSeq++,this._open=!1,this._task=null,this._error="",this._toastTimer.clear(),this._toast=""}_showToast(e,t){this._toast=e,this._toastTimer.schedule(()=>{this._toast=""},t)}async _loadTask(){let e=this._entryId,t=this._taskId;if(!e||!t)return;let i=++this._loadSeq,a="",o=await j(this,{type:"maintenance_supporter/object",entry_id:e},{onError:p=>{a=p}});if(i!==this._loadSeq)return;if(o===void 0){this._error=a;return}this._objectName=o?.object?.name||"";let d=(o?.tasks||[]).find(p=>p.id===t);this._task=d??null,this._taskRef=Fn(o?.object,d),d||(this._error=n("ws_err_not_found",this._lang))}_runWs(e){return this._error="",j(this,e,{busy:t=>{this._busy=t},onError:t=>{this._error=t}})}_notifyChanged(e){this.dispatchEvent(new CustomEvent("task-action-fired",{detail:{entry_id:this._entryId,task_id:this._taskId,action:e},bubbles:!0,composed:!0}))}_onComplete(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(oe(),pe)).then(async({openCompleteDialog:e})=>{let t=this._task,i=[];try{i=(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects",compact:!0})).objects||[]}catch{}e(Dn({entryId:this._entryId,taskId:this._taskId,taskName:t.name,task:t,objects:i,lang:this._lang,features:this._features,currencySymbol:this._currencySymbol,partsCostMode:this._partsCostMode}))&&(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})!==void 0&&(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})!==void 0&&(this._notifyChanged("reset"),this.close())}async _onPostponeConfirm(){if(!this._entryId||!this._taskId||!this._postponeDate)return;await this._runWs({type:"maintenance_supporter/task/postpone",entry_id:this._entryId,task_id:this._taskId,until:this._postponeDate})!==void 0&&(this._notifyChanged("postpone"),this.close())}async _onSnooze(){if(!this._entryId||!this._taskId)return;let e=await this._runWs({type:"maintenance_supporter/task/snooze",entry_id:this._entryId,task_id:this._taskId});e!==void 0&&(this._notifyChanged("snooze"),this.dispatchEvent(new CustomEvent("hass-notification",{detail:{message:On(e,this._lang)},bubbles:!0,composed:!0})),this.close())}_onEdit(){!this._entryId||!this._taskId||Promise.resolve().then(()=>(oe(),pe)).then(({openEditTaskDialog:e})=>{e(this._entryId,this._taskId),this.close()})}_onQr(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(oe(),pe)).then(({openQrDialog:e})=>{e({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||!await ze(this.hass,{title:n("delete",this._lang),message:n("delete_task_confirm",this._lang),confirmText:n("delete",this._lang),danger:!0}))return;await this._runWs({type:"maintenance_supporter/task/delete",entry_id:this._entryId,task_id:this._taskId})!==void 0&&(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})!==void 0&&
Line truncated
|
||||
<div class="recommendation-card">
|
||||
<h4>${n("suggested_interval",t)}</h4>
|
||||
${Xn(e.interval_days,e.suggested_interval,e.interval_confidence||"medium",t)}
|
||||
${ia(e.interval_days,e.suggested_interval,e.interval_confidence||"medium",t)}
|
||||
<div class="recommendation-actions">
|
||||
${xe(this.hass?.user,this._access)?l`<button class="btn primary qa-apply-suggestion"
|
||||
@click=${this._applySuggestion} ?disabled=${this._busy}>
|
||||
@@ -4436,14 +4475,14 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
<div class="adaptive-stack">
|
||||
${this._toast?l`<div class="toast">${this._toast}</div>`:h}
|
||||
${i?this._renderRecommendation(e):h}
|
||||
${a?Zn(e,t,this._features):h}
|
||||
${o?Yn(e,t):h}
|
||||
${a?ea(e,t,this._features):h}
|
||||
${o?Jn(e,t):h}
|
||||
${d?l`
|
||||
${ra(e,t,this._features)}
|
||||
${e.seasonal_factors?.length===12||e.interval_analysis?.seasonal_factors?.length===12?sa(e,t):h}
|
||||
${aa(e,t,this._features)}
|
||||
${e.seasonal_factors?.length===12||e.interval_analysis?.seasonal_factors?.length===12?oa(e,t):h}
|
||||
`:h}
|
||||
</div>
|
||||
`}_renderDetails(e){let t=this._lang,i=e.history||[],a=e.history_count??i.length,o=Bn(i).slice(0,20);return l`
|
||||
`}_renderDetails(e){let t=this._lang,i=e.history||[],a=e.history_count??i.length,o=Gn(i).slice(0,20);return l`
|
||||
<div class="details">
|
||||
<div class="stats-grid">
|
||||
<div class="stat">
|
||||
@@ -4456,7 +4495,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">${n("avg_duration",t)}</span>
|
||||
<span class="stat-value">${Qt(e.average_duration!=null?Math.round(e.average_duration):null,t)}</span>
|
||||
<span class="stat-value">${Zt(e.average_duration!=null?Math.round(e.average_duration):null,t)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="history-header">
|
||||
@@ -4465,7 +4504,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
</div>
|
||||
${i.length===0?l`<div class="history-empty">${n("history_empty",t)}</div>`:l`
|
||||
<div class="history-list">
|
||||
${o.map(d=>Wn(d,{lang:t,hass:this.hass,currencySymbol:this._currencySymbol,openEdit:xe(this.hass?.user,this._access)?p=>this._onEditHistoryEntry(p):void 0,readingUnit:e.reading_unit,readingSlotDelta:(p,_)=>Ps(i,p,_),taskRef:this._taskRef},{compact:!0}))}
|
||||
${o.map(d=>Kn(d,{lang:t,hass:this.hass,currencySymbol:this._currencySymbol,openEdit:xe(this.hass?.user,this._access)?p=>this._onEditHistoryEntry(p):void 0,readingUnit:e.reading_unit,readingSlotDelta:(p,_)=>Ls(i,p,_),taskRef:this._taskRef},{compact:!0}))}
|
||||
${a>o.length?l`<div class="history-more">… +${a-o.length} ${n("older_entries",t)}</div>`:h}
|
||||
</div>
|
||||
`}
|
||||
@@ -4474,9 +4513,9 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
${t?l`
|
||||
<div class="header">
|
||||
<div class="title">
|
||||
<span class="status-dot" style="background: ${pi(t)}"></span>
|
||||
<span class="status-dot" style="background: ${hi(t)}"></span>
|
||||
<span class="task-name">${t.name}</span>
|
||||
${Gn(t,e)}
|
||||
${Qn(t,e)}
|
||||
</div>
|
||||
<div class="object">
|
||||
<button class="link-inline" @click=${()=>{this._entryId&&Promise.resolve().then(()=>(oe(),pe)).then(({openObjectQuickActions:o})=>{o(this._entryId),this.close()})}}>${this._objectName}</button>
|
||||
@@ -4484,10 +4523,10 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
<div class="quick-info">
|
||||
${t.next_due?l`<span><strong>${n("next_due",e)}:</strong> ${O(t.next_due,e)}</span>`:h}
|
||||
${t.last_performed?l`<span><strong>${n("last_performed",e)}:</strong> ${O(t.last_performed,e)}</span>`:h}
|
||||
${t.schedule?.kind&&!["manual","one_time"].includes(t.schedule.kind)||t.interval_days!=null?l`<span><strong>${n("interval",e)}:</strong> ${$s(t,e)}</span>`:h}
|
||||
${t.schedule?.kind&&!["manual","one_time"].includes(t.schedule.kind)||t.interval_days!=null?l`<span><strong>${n("interval",e)}:</strong> ${Es(t,e)}</span>`:h}
|
||||
${Ct(t)?l`<span><strong>${n("phase_current",e)}:</strong> ${Ct(t)}</span>`:h}
|
||||
</div>
|
||||
${t.notes?l`<div class="notes-body">${di(t.notes)}</div>`:h}
|
||||
${t.notes?l`<div class="notes-body">${ui(t.notes)}</div>`:h}
|
||||
</div>
|
||||
|
||||
${this._error?l`<div class="error">${this._error}</div>`:h}
|
||||
@@ -4614,7 +4653,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
</div>
|
||||
`}
|
||||
`:this._error?l`<div class="error">${this._error}</div>`:l`<div class="loading">${n("loading",e)}</div>`}
|
||||
`)}updated(e){e.has("_open")&&this._open&&nt(this.shadowRoot)}};R.styles=[Zt,rt,E`
|
||||
`)}updated(e){e.has("_open")&&this._open&&nt(this.shadowRoot)}};P.styles=[Jt,rt,E`
|
||||
:host { display: contents; --ms-modal-max-width: 460px; }
|
||||
.header { display: flex; flex-direction: column; gap: 6px; }
|
||||
.title { display: flex; align-items: center; gap: 10px; }
|
||||
@@ -4772,7 +4811,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; }
|
||||
`],c([v({attribute:!1})],R.prototype,"hass",2),c([u()],R.prototype,"_open",2),c([u()],R.prototype,"_entryId",2),c([u()],R.prototype,"_taskId",2),c([u()],R.prototype,"_task",2),c([u()],R.prototype,"_objectName",2),c([u()],R.prototype,"_taskRef",2),c([u()],R.prototype,"_busy",2),c([u()],R.prototype,"_error",2),c([u()],R.prototype,"_showSkip",2),c([u()],R.prototype,"_showReset",2),c([u()],R.prototype,"_showPostpone",2),c([u()],R.prototype,"_showDetails",2),c([u()],R.prototype,"_showAdaptive",2),c([u()],R.prototype,"_skipReason",2),c([u()],R.prototype,"_resetDate",2),c([u()],R.prototype,"_postponeDate",2),c([u()],R.prototype,"_features",2),c([u()],R.prototype,"_toast",2),c([u()],R.prototype,"_access",2),c([u()],R.prototype,"_partsCostMode",2);customElements.get("maintenance-task-quick-actions-dialog")||customElements.define("maintenance-task-quick-actions-dialog",R)});function oa(s){return!!s&&/^https?:\/\//i.test(s)}var la=y(()=>{"use strict"});var se,ca=y(()=>{"use strict";I();la();dr();F();T();De();hr();St();xt();ci();li();se=class extends S{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._data=null;this._busy=!1;this._error="";this._access=je}get _lang(){return N(this.hass)}async openFor(e){this._entryId=e,this._error="",this._open=!0,await Promise.all([this._load(),ne(this.hass).then(t=>{this._access=t.access})])}close(){this._open=!1,this._data=null,this._error=""}async _load(){if(!this._entryId)return;let e=await j(this,{type:"maintenance_supporter/object",entry_id:this._entryId},{onError:t=>{this._error=t}});e!==void 0&&(this._data=e)}_runWs(e){return this._error="",j(this,e,{busy:t=>{this._busy=t},onError:t=>{this._error=t}})}_onEditObject(){!this._entryId||!this._data||Promise.resolve().then(()=>(oe(),pe)).then(({openEditObjectDialog:e})=>{e(this._entryId,this._data.object),this.close()})}_onAddTask(){this._entryId&&Promise.resolve().then(()=>(oe(),pe)).then(({openCreateTaskDialog:e})=>{e(this._entryId),this.close()})}async _onDelete(){if(!this._entryId||!this._data||!await ze(this.hass,{title:n("delete",this._lang),message:n("delete_object_confirm",this._lang),confirmText:n("delete",this._lang),danger:!0}))return;let t=this._entryId;await this._runWs({type:"maintenance_supporter/object/delete",entry_id:t})!==void 0&&(this.dispatchEvent(new CustomEvent("object-deleted",{detail:{entry_id:t},bubbles:!0,composed:!0})),this.close())}async _onArchiveObject(){if(!this._entryId||!this._data)return;let e=!!this._data.object.archived;if(!e&&!await ze(this.hass,{title:n("archive_object",this._lang),message:n("confirm_archive_object",this._lang),confirmText:n("archive_object",this._lang)}))return;let t=this._entryId;await this._runWs({type:e?"maintenance_supporter/object/unarchive":"maintenance_supporter/object/archive",entry_id:t})!==void 0&&(this.dispatchEvent(new CustomEvent("object-changed",{detail:{entry_id:t},bubbles:!0,composed:!0})),this.close())}_onTaskClick(e){this._entryId&&Promise.resolve().then(()=>(oe(),pe)).then(({openTaskQuickActions:t})=>{t(this._entryId,e)})}render(){if(!this._open)return h;let e=this._lang,t=this._data,i=t?.object,a=t?.tasks||[],o=xe(this.hass?.user,this._access);return st(()=>this.close(),l`
|
||||
`],c([v({attribute:!1})],P.prototype,"hass",2),c([u()],P.prototype,"_open",2),c([u()],P.prototype,"_entryId",2),c([u()],P.prototype,"_taskId",2),c([u()],P.prototype,"_task",2),c([u()],P.prototype,"_objectName",2),c([u()],P.prototype,"_taskRef",2),c([u()],P.prototype,"_busy",2),c([u()],P.prototype,"_error",2),c([u()],P.prototype,"_showSkip",2),c([u()],P.prototype,"_showReset",2),c([u()],P.prototype,"_showPostpone",2),c([u()],P.prototype,"_showDetails",2),c([u()],P.prototype,"_showAdaptive",2),c([u()],P.prototype,"_skipReason",2),c([u()],P.prototype,"_resetDate",2),c([u()],P.prototype,"_postponeDate",2),c([u()],P.prototype,"_features",2),c([u()],P.prototype,"_toast",2),c([u()],P.prototype,"_access",2),c([u()],P.prototype,"_partsCostMode",2);customElements.get("maintenance-task-quick-actions-dialog")||customElements.define("maintenance-task-quick-actions-dialog",P)});function da(s){return!!s&&/^https?:\/\//i.test(s)}var pa=y(()=>{"use strict"});var ne,ua=y(()=>{"use strict";I();pa();pr();F();T();De();_r();St();xt();pi();di();ne=class extends S{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._data=null;this._busy=!1;this._error="";this._access=je}get _lang(){return N(this.hass)}async openFor(e){this._entryId=e,this._error="",this._open=!0,await Promise.all([this._load(),se(this.hass).then(t=>{this._access=t.access})])}close(){this._open=!1,this._data=null,this._error=""}async _load(){if(!this._entryId)return;let e=await j(this,{type:"maintenance_supporter/object",entry_id:this._entryId},{onError:t=>{this._error=t}});e!==void 0&&(this._data=e)}_runWs(e){return this._error="",j(this,e,{busy:t=>{this._busy=t},onError:t=>{this._error=t}})}_onEditObject(){!this._entryId||!this._data||Promise.resolve().then(()=>(oe(),pe)).then(({openEditObjectDialog:e})=>{e(this._entryId,this._data.object),this.close()})}_onAddTask(){this._entryId&&Promise.resolve().then(()=>(oe(),pe)).then(({openCreateTaskDialog:e})=>{e(this._entryId),this.close()})}async _onDelete(){if(!this._entryId||!this._data||!await ze(this.hass,{title:n("delete",this._lang),message:n("delete_object_confirm",this._lang),confirmText:n("delete",this._lang),danger:!0}))return;let t=this._entryId;await this._runWs({type:"maintenance_supporter/object/delete",entry_id:t})!==void 0&&(this.dispatchEvent(new CustomEvent("object-deleted",{detail:{entry_id:t},bubbles:!0,composed:!0})),this.close())}async _onArchiveObject(){if(!this._entryId||!this._data)return;let e=!!this._data.object.archived;if(!e&&!await ze(this.hass,{title:n("archive_object",this._lang),message:n("confirm_archive_object",this._lang),confirmText:n("archive_object",this._lang)}))return;let t=this._entryId;await this._runWs({type:e?"maintenance_supporter/object/unarchive":"maintenance_supporter/object/archive",entry_id:t})!==void 0&&(this.dispatchEvent(new CustomEvent("object-changed",{detail:{entry_id:t},bubbles:!0,composed:!0})),this.close())}_onTaskClick(e){this._entryId&&Promise.resolve().then(()=>(oe(),pe)).then(({openTaskQuickActions:t})=>{t(this._entryId,e)})}render(){if(!this._open)return h;let e=this._lang,t=this._data,i=t?.object,a=t?.tasks||[],o=xe(this.hass?.user,this._access);return st(()=>this.close(),l`
|
||||
${t&&i?l`
|
||||
<div class="header">
|
||||
<div class="title">${i.name}</div>
|
||||
@@ -4790,9 +4829,9 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
<div class="task-list">
|
||||
${a.map(d=>l`
|
||||
<div class="task-row" @click=${()=>this._onTaskClick(d.id)}>
|
||||
<span class="status-dot" style="background: ${pi(d)}"></span>
|
||||
<span class="status-dot" style="background: ${hi(d)}"></span>
|
||||
<span class="task-name">${d.name}</span>
|
||||
<span class="task-status ${It(d)}">${ur(It(d),e)}</span>
|
||||
<span class="task-status ${It(d)}">${hr(It(d),e)}</span>
|
||||
</div>
|
||||
`)}
|
||||
</div>
|
||||
@@ -4802,7 +4841,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
${i.notes?l`
|
||||
<div class="notes-section">
|
||||
<strong>${n("object_notes_label",e)}</strong>
|
||||
<div class="notes-body">${di(i.notes)}</div>
|
||||
<div class="notes-body">${ui(i.notes)}</div>
|
||||
</div>
|
||||
`:h}
|
||||
|
||||
@@ -4832,11 +4871,11 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
${i.map(([a,o])=>l`
|
||||
<div class="meta-item">
|
||||
<span class="meta-label">${a}</span>
|
||||
<span class="meta-value">${oa(o)?l`<a href="${o}" target="_blank" rel="noopener noreferrer">${o}</a>`:o}</span>
|
||||
<span class="meta-value">${da(o)?l`<a href="${o}" target="_blank" rel="noopener noreferrer">${o}</a>`:o}</span>
|
||||
</div>
|
||||
`)}
|
||||
</div>
|
||||
`}};se.styles=[rt,E`
|
||||
`}};ne.styles=[rt,E`
|
||||
:host { display: contents; }
|
||||
.header { display: flex; flex-direction: column; gap: 6px; }
|
||||
.title { font-size: 20px; font-weight: 600; }
|
||||
@@ -4881,7 +4920,7 @@ ${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; }
|
||||
`],c([v({attribute:!1})],se.prototype,"hass",2),c([u()],se.prototype,"_open",2),c([u()],se.prototype,"_entryId",2),c([u()],se.prototype,"_data",2),c([u()],se.prototype,"_busy",2),c([u()],se.prototype,"_error",2),c([u()],se.prototype,"_access",2);customElements.get("maintenance-object-quick-actions-dialog")||customElements.define("maintenance-object-quick-actions-dialog",se)});var pe={};ma(pe,{__resetSettingsCacheForTests:()=>nl,getRowActionStyle:()=>sl,openCompleteDialog:()=>dl,openCreateObjectDialog:()=>al,openCreateTaskDialog:()=>ll,openEditObjectDialog:()=>ol,openEditTaskDialog:()=>cl,openHistoryEditDialog:()=>mr,openObjectQuickActions:()=>ul,openQrDialog:()=>pl,openTaskQuickActions:()=>gr});function hi(){return document.querySelector("home-assistant")?.hass}function rl(){return document.querySelector("home-assistant")?.shadowRoot??document.body}function ue(s){let r=rl(),e=r.querySelector(s)??document.body.querySelector(s);return e?e.parentNode!==r&&r.appendChild(e):(e=document.createElement(s),r.appendChild(e)),e}function he(s){let r=hi();if(!r)return!1;s.hass=r;let e=N(r);return Le(e)||Ne(e).then(()=>{s.requestUpdate?.()}),zi(r.locale,r.config?.country,r.config?.time_zone),!0}function sl(s){return ne(s).then(r=>r.rowActionStyle)}function nl(){Ms()}function al(){let s=ue(da);return he(s)?(s.openCreate(),!0):!1}function ol(s,r){let e=ue(da);return he(e)?(e.openEdit(s,r),!0):!1}function ll(s="",r){let e=ue(pa);if(!he(e))return!1;let t=hi();return t?((async()=>{let i=await ne(t),a=e;ua(a,i),a.openCreate(s,r)})(),!0):!1}function ua(s,r){s.checklistsEnabled=r.features.checklists,s.scheduleTimeEnabled=r.features.schedule_time,s.completionActionsEnabled=r.features.completion_actions,s.adaptiveFeature=r.features.adaptive,s.seasonalFeature=r.features.seasonal,s.environmentalFeature=r.features.environmental,s.defaultWarningDays=r.defaultWarningDays}function cl(s,r){let e=ue(pa);if(!he(e))return!1;let t=hi();return t?((async()=>{try{let[i,a]=await Promise.all([t.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:s}),ne(t)]),o=(i.tasks||[]).find(p=>p.id===r);if(!o){console.warn(`openEditTaskDialog: task ${r} not found in entry ${s}`);return}let d=e;ua(d,a),await d.openEdit(s,o)}catch(i){console.warn("openEditTaskDialog: failed to load task/features",i)}})(),!0):!1}function mr(s){let r=ue(Jo);return he(r)?(r.openEdit(s),!0):!1}function dl(s){let r=ue(Xo);return he(r)?(On(r,s,N(hi())),!0):!1}function pl(s){let r=ue(el);return he(r)?(r.openForTask(s.entry_id,s.task_id,s.object_name,s.task_name),!0):!1}function gr(s,r){let e=ue(tl);return he(e)?(e.openFor(s,r),!0):!1}function ul(s){let r=ue(il);return he(r)?(r.openFor(s),!0):!1}var da,pa,Jo,Xo,el,tl,il,oe=y(()=>{"use strict";Vs();pn();En();Pn();Ln();aa();ca();T();cr();St();da="maintenance-object-dialog",pa="maintenance-task-dialog",Jo="maintenance-history-edit-dialog",Xo="maintenance-complete-dialog",el="maintenance-qr-dialog",tl="maintenance-task-quick-actions-dialog",il="maintenance-object-quick-actions-dialog"});I();F();Mt();var Ra={days:1,weeks:7,months:30.4368,years:365.25};function Gr(s,r){return!s||s<=0?0:s*(Ra[r||"days"]??1)}zt();Z();var rs=5;function ja(s,r){let e=[];for(let t=0;t<r;t++)e.push(Re(s,t));return e}function Da(s){if(!s||s.length===0)return null;let r=s.map(e=>Dt(e)).filter(e=>e!==null&&e>=0);return r.length===0?null:r.reduce((e,t)=>e+t,0)/r.length}function Ma(s){let{windowStart:r,windowEnd:e,task:t,entryId:i,objectName:a}=s,o=[],d=(f,x)=>({date:f,entry_id:i,task_id:t.id,task_name:t.name,object_name:a,status:x&&(t.status==="overdue"||t.status==="triggered")?"ok":t.status,days_until_due:x?null:t.days_until_due??null,projected:x,schedule_type:t.schedule_type,interval_days:t.interval_days??null,interval_unit:t.interval_unit??null,responsible_user_id:t.responsible_user_id??null,avg_cost:Da(t.history),adaptive_enabled:!!t.adaptive_config?.enabled,prediction_confidence:t.threshold_prediction_confidence??null}),p=Math.max(1,Math.round(Gr(t.interval_days,t.interval_unit)));if(t.status==="overdue"||t.status==="triggered"){if(o.push(d(r,!1)),t.schedule_type==="time_based"&&t.interval_days&&t.interval_days>0){let f=Re(r,p),x=1;for(;f<=e&&x<rs;)o.push(d(f,!0)),x++,f=Re(f,p)}return o}let _=t.next_due;if(typeof _!="string"||!_)return o;let m=_.slice(0,10);if(!/^\d{4}-\d{2}-\d{2}$/.test(m))return o;if(m>=r&&m<=e)o.push(d(m,!1));else if(m>e)return o;if(t.schedule_type==="time_based"&&t.interval_days&&t.interval_days>0){let f=Re(m,p),x=o.length;for(;f<=e&&x<rs;)f>=r&&(o.push(d(f,!0)),x++),f=Re(f,p)}return o}function ss(s,r,e,t=null){let i=ja(r,e),a=i[0],o=i[i.length-1],d=[];for(let _ of s){let m=_.object?.name||"",f=_.entry_id,x=_.tasks||[];for(let w of x){if(t&&w.responsible_user_id!==t||w.enabled===!1)continue;let k=Ma({windowStart:a,windowEnd:o,task:w,entryId:f,objectName:m});d.push(...k)}}let p=new Map;for(let _ of i)p.set(_,[]);for(let _ of d){let m=p.get(_.date);m&&m.push(_)}for(let[,_]of p)_.sort((m,f)=>{let x=Ii(m.status),w=Ii(f.
Line truncated
|
||||
`],c([v({attribute:!1})],ne.prototype,"hass",2),c([u()],ne.prototype,"_open",2),c([u()],ne.prototype,"_entryId",2),c([u()],ne.prototype,"_data",2),c([u()],ne.prototype,"_busy",2),c([u()],ne.prototype,"_error",2),c([u()],ne.prototype,"_access",2);customElements.get("maintenance-object-quick-actions-dialog")||customElements.define("maintenance-object-quick-actions-dialog",ne)});var pe={};ya(pe,{__resetSettingsCacheForTests:()=>cl,getRowActionStyle:()=>ll,openCompleteDialog:()=>_l,openCreateObjectDialog:()=>dl,openCreateTaskDialog:()=>ul,openEditObjectDialog:()=>pl,openEditTaskDialog:()=>hl,openHistoryEditDialog:()=>gr,openObjectQuickActions:()=>gl,openQrDialog:()=>ml,openTaskQuickActions:()=>fr});function Rt(){return document.querySelector("home-assistant")?.hass}function ol(){return document.querySelector("home-assistant")?.shadowRoot??document.body}function ue(s){let r=ol(),e=r.querySelector(s)??document.body.querySelector(s);return e?e.parentNode!==r&&r.appendChild(e):(e=document.createElement(s),r.appendChild(e)),e}function he(s){let r=Rt();if(!r)return!1;s.hass=r;let e=N(r);return Le(e)||Ne(e).then(()=>{s.requestUpdate?.()}),Fi(r.locale,r.config?.country,r.config?.time_zone),!0}function ll(s){return se(s).then(r=>r.rowActionStyle)}function cl(){zs()}function dl(){let s=ue(ha);return he(s)?(s.openCreate(),ma(s),!0):!1}async function ma(s){let r=Rt();r&&(s.placesEnabled=(await se(r)).features.places)}function pl(s,r){let e=ue(ha);return he(e)?(e.openEdit(s,r),ma(e),!0):!1}function ul(s="",r){let e=ue(_a);if(!he(e))return!1;let t=Rt();return t?((async()=>{let i=await se(t),a=e;ga(a,i),a.openCreate(s,r)})(),!0):!1}function ga(s,r){s.checklistsEnabled=r.features.checklists,s.scheduleTimeEnabled=r.features.schedule_time,s.completionActionsEnabled=r.features.completion_actions,s.adaptiveFeature=r.features.adaptive,s.seasonalFeature=r.features.seasonal,s.environmentalFeature=r.features.environmental,s.defaultWarningDays=r.defaultWarningDays}function hl(s,r){let e=ue(_a);if(!he(e))return!1;let t=Rt();return t?((async()=>{try{let[i,a]=await Promise.all([t.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:s}),se(t)]),o=(i.tasks||[]).find(p=>p.id===r);if(!o){console.warn(`openEditTaskDialog: task ${r} not found in entry ${s}`);return}let d=e;ga(d,a),await d.openEdit(s,o)}catch(i){console.warn("openEditTaskDialog: failed to load task/features",i)}})(),!0):!1}function gr(s){let r=ue(il);return he(r)?(r.openEdit(s),!0):!1}function _l(s){let r=ue(rl);return he(r)?(Mn(r,s,N(Rt())),!0):!1}function ml(s){let r=ue(sl);return he(r)?(r.openForTask(s.entry_id,s.task_id,s.object_name,s.task_name),!0):!1}function fr(s,r){let e=ue(nl);return he(e)?(e.openFor(s,r),!0):!1}function gl(s){let r=ue(al);return he(r)?(r.openFor(s),!0):!1}var ha,_a,il,rl,sl,nl,al,oe=y(()=>{"use strict";Gs();_n();An();qn();Hn();ca();ua();T();dr();St();ha="maintenance-object-dialog",_a="maintenance-task-dialog",il="maintenance-history-edit-dialog",rl="maintenance-complete-dialog",sl="maintenance-qr-dialog",nl="maintenance-task-quick-actions-dialog",al="maintenance-object-quick-actions-dialog"});I();F();zt();var qa={days:1,weeks:7,months:30.4368,years:365.25};function Kr(s,r){return!s||s<=0?0:s*(qa[r||"days"]??1)}Ft();Z();var ss=5;function Fa(s,r){let e=[];for(let t=0;t<r;t++)e.push(Re(s,t));return e}function Ua(s){if(!s||s.length===0)return null;let r=s.map(e=>Mt(e)).filter(e=>e!==null&&e>=0);return r.length===0?null:r.reduce((e,t)=>e+t,0)/r.length}function Ba(s){let{windowStart:r,windowEnd:e,task:t,entryId:i,objectName:a}=s,o=[],d=(f,x)=>({date:f,entry_id:i,task_id:t.id,task_name:t.name,object_name:a,status:x&&(t.status==="overdue"||t.status==="triggered")?"ok":t.status,days_until_due:x?null:t.days_until_due??null,projected:x,schedule_type:t.schedule_type,interval_days:t.interval_days??null,interval_unit:t.interval_unit??null,responsible_user_id:t.responsible_user_id??null,avg_cost:Ua(t.history),adaptive_enabled:!!t.adaptive_config?.enabled,prediction_confidence:t.threshold_prediction_confidence??null}),p=Math.max(1,Math.round(Kr(t.interval_days,t.interval_unit)));if(t.status==="overdue"||t.status==="triggered"){if(o.push(d(r,!1)),t.schedule_type==="time_based"&&t.interval_days&&t.interval_days>0){let f=Re(r,p),x=1;for(;f<=e&&x<ss;)o.push(d(f,!0)),x++,f=Re(f,p)}return o}let _=t.next_due;if(typeof _!="string"||!_)return o;let m=_.slice(0,10);if(!/^\d{4}-\d{2}-\d{2}$/.test(m))return o;if(m>=r&&m<=e)o.push(d(m,!1));else if(m>e)return o;if(t.schedule_type==="time_based"&&t.interval_days&&t.interval_days>0){let f=Re(m,p),x=o.length;for(;f<=e&&x<ss;)f>=r&&(o.push(d(f,!0)),x++),f=Re(f,p)}return o}function ns(s,r,e,t=null){let i=Fa(r,e),a=i[0],o=i[i.length-1],d=[];for(let _ of s){let m=_.object?.name||"",f=_.entry_id,x=_.tasks||[];for(let w of x){if(t&&w.responsible_user_id!==t||w.enabled===!1)continue;let k=Ba({windowStart:a,windowEnd:o,task:w,entryId:f,objectName:m});d.push(...k)}}let p=new Map;for(let _ of i)p.set(_,[]);for(let _ o
Line truncated
|
||||
.cal-controls {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
@@ -5078,24 +5117,24 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
.cal-pill-day { font-size: 17px; }
|
||||
.cal-user-filter { margin-left: 0; width: 100%; }
|
||||
}
|
||||
`;T();function Ss(s){let r=window;r.customCards=r.customCards||[],r.customCards.some(e=>e.type===s.type)||r.customCards.push(s)}function Ts(s,r,e=t=>import(t)){let t=window,i;try{let m=new URL(r);i=m.origin+m.pathname}catch{return}let a=`__msCardHeal:${s[0]}:${i}`;if(t[a])return;t[a]=!0;let o=0,d=0,p=!1,_=()=>{if(o+=1,s.some(f=>!customElements.get(f))&&d<3&&!p){d+=1,p=!0;try{let f=new URL(r);f.searchParams.set("heal",`${Date.now()}`),e(f.href).catch(()=>{}).finally(()=>{p=!1})}catch{return}}o<20&&window.setTimeout(_,o<8?500:2e3)};window.setTimeout(_,250)}Wi();xt();St();oe();var G=class extends S{constructor(){super(...arguments);this._config={type:"custom:maintenance-supporter-calendar-card"};this._objects=[];this._stats=null;this._windowDays=30;this._pastDays=0;this._userFilter="";this._objectFilter="";this._configuredObjects=[];this._unsub=null;this._pastHistory={};this._pastHistorySig="";this._pastSeq=0;this._dataLoaded=!1;this._lastConnection=null;this._onHistorySaved=e=>{let t=e.detail;!t?.entry_id||!t.task_id||!(Bt(t.entry_id,t.task_id)in this._pastHistory)||(this._pastHistorySig="",this._loadPastHistories())}}static getConfigElement(){return document.createElement("maintenance-supporter-calendar-card-editor")}static getStubConfig(){return{type:"custom:maintenance-supporter-calendar-card",window_days:30,show_window_chips:!0,show_user_filter:!0}}setConfig(e){if(this._config={...e},this._windowDays=e.window_days&&[7,14,30,365].includes(e.window_days)?e.window_days:30,this._pastDays=e.past_days&&[30,90].includes(e.past_days)?e.past_days:0,this._userFilter=typeof e.user_filter=="string"?e.user_filter:"",typeof e.object_filter=="string")this._objectFilter=e.object_filter,this._configuredObjects=[];else if(Array.isArray(e.object_filter)){let t=e.object_filter.filter(i=>typeof i=="string"&&i!=="");this._objectFilter=t.length===1?t[0]:"",this._configuredObjects=t.length>1?t:[]}else this._objectFilter="",this._configuredObjects=[]}getCardSize(){return 6}get _lang(){return N(this.hass)}connectedCallback(){super.connectedCallback(),window.addEventListener("history-entry-saved",this._onHistorySaved)}disconnectedCallback(){if(super.disconnectedCallback(),window.removeEventListener("history-entry-saved",this._onHistorySaved),this._unsub){try{this._unsub()}catch{}this._unsub=null}this._dataLoaded=!1,this._lastConnection=null}updated(e){if(super.updated(e),gs(this,e),this.hass&&this._pastDays>0&&(e.has("_objects")||e.has("_pastDays"))&&this._loadPastHistories(),e.has("hass")&&this.hass){if(!this._dataLoaded)this._dataLoaded=!0,this._lastConnection=this.hass.connection,this._loadData(),this._subscribe();else if(this.hass.connection!==this._lastConnection){if(this._lastConnection=this.hass.connection,this._unsub){try{this._unsub()}catch{}this._unsub=null}this._subscribe(),this._loadData()}}}async _loadData(){try{let[e,t]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"}),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/statistics"})]);this._objects=e.objects,this._stats=t,Vt(this._stats.budget)}catch{}}async _loadPastHistories(){let e=ns(this._objects,Ie(),this._pastDays||30),t=e.map(o=>o.sig).join("|");if(t===this._pastHistorySig)return;this._pastHistorySig=t;let i=++this._pastSeq,a=await Promise.all(e.map(async o=>{try{let d=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/history",entry_id:o.entryId,task_id:o.taskId});return[o.key,d.history??[]]}catch{return null}}));i===this._pastSeq&&(this._pastHistory=Object.fromEntries(a.filter(o=>o!==null)))}async _subscribe(){try{let e=await this.hass.connection.subscribeMessage(t=>{let i=t;this._objects=i.objects},{type:"maintenance_supporter/subscribe"});if(!this.isConnected){e();return}this._unsub=e}catch{}}_onEventClick(e){if(e.history_timestamp){this._openHistoryEntry(e);return}this._openTask(e)}_openTask(e){gr(e.entry_id,e.task_id)||this.dispatchEvent(new CustomEvent("ll-custom",{detail:{type:"maintenance-supporter:open-task",entry_id:e.entry_id,task_id:e.task_id},bubbles:!0,composed:!0}))}async _openHistoryEntry(e){try{let t=await ne(this.hass);if(!xe(this.hass?.user,t.access)){this._openTask(e);return}}catch{}try{let t=await qs(this.hass,e.entry_id,e.task_id,e.history_timestamp);if(!t||mr(t))return}catch{}this.dispatchEvent(new CustomEvent("ll-custom",{detail:{type:"maintenance-supporter:edit-history",entry_id:e.entry_id,task_id:e.task_id,original_timestamp:e.history_timestamp},bubbles:!0,composed:!0}))}render(){if(!this.hass)return h;let e=this._lang,t=this._config.show_window_chips!==!1,i=this._config.show_user_filter!==!1,a=this._config.title,o=null;this._userFilter&&(o=this._userFilter==="current_user"?this.hass?.user?.id??null:this._userFilter);let d=b=>{let le=b.toLowerCase();return this._objects.find(_e=>_e.entry_id===b||_e.object.name.toLowerCase()===le)?.entry_id??null},p=new Set(this._configuredObjects.map(d).filter(b=>b!==null)),_=p.size?this._objects.filter(b
Line truncated
|
||||
`;T();function Ts(s){let r=window;r.customCards=r.customCards||[],r.customCards.some(e=>e.type===s.type)||r.customCards.push(s)}function As(s,r,e=t=>import(t)){let t=window,i;try{let m=new URL(r);i=m.origin+m.pathname}catch{return}let a=`__msCardHeal:${s[0]}:${i}`;if(t[a])return;t[a]=!0;let o=0,d=0,p=!1,_=()=>{if(o+=1,s.some(f=>!customElements.get(f))&&d<3&&!p){d+=1,p=!0;try{let f=new URL(r);f.searchParams.set("heal",`${Date.now()}`),e(f.href).catch(()=>{}).finally(()=>{p=!1})}catch{return}}o<20&&window.setTimeout(_,o<8?500:2e3)};window.setTimeout(_,250)}Vi();xt();St();oe();var G=class extends S{constructor(){super(...arguments);this._config={type:"custom:maintenance-supporter-calendar-card"};this._objects=[];this._stats=null;this._windowDays=30;this._pastDays=0;this._userFilter="";this._objectFilter="";this._configuredObjects=[];this._unsub=null;this._pastHistory={};this._pastHistorySig="";this._pastSeq=0;this._dataLoaded=!1;this._lastConnection=null;this._onHistorySaved=e=>{let t=e.detail;!t?.entry_id||!t.task_id||!(Wt(t.entry_id,t.task_id)in this._pastHistory)||(this._pastHistorySig="",this._loadPastHistories())}}static getConfigElement(){return document.createElement("maintenance-supporter-calendar-card-editor")}static getStubConfig(){return{type:"custom:maintenance-supporter-calendar-card",window_days:30,show_window_chips:!0,show_user_filter:!0}}setConfig(e){if(this._config={...e},this._windowDays=e.window_days&&[7,14,30,365].includes(e.window_days)?e.window_days:30,this._pastDays=e.past_days&&[30,90].includes(e.past_days)?e.past_days:0,this._userFilter=typeof e.user_filter=="string"?e.user_filter:"",typeof e.object_filter=="string")this._objectFilter=e.object_filter,this._configuredObjects=[];else if(Array.isArray(e.object_filter)){let t=e.object_filter.filter(i=>typeof i=="string"&&i!=="");this._objectFilter=t.length===1?t[0]:"",this._configuredObjects=t.length>1?t:[]}else this._objectFilter="",this._configuredObjects=[]}getCardSize(){return 6}get _lang(){return N(this.hass)}connectedCallback(){super.connectedCallback(),window.addEventListener("history-entry-saved",this._onHistorySaved)}disconnectedCallback(){if(super.disconnectedCallback(),window.removeEventListener("history-entry-saved",this._onHistorySaved),this._unsub){try{this._unsub()}catch{}this._unsub=null}this._dataLoaded=!1,this._lastConnection=null}updated(e){if(super.updated(e),fs(this,e),this.hass&&this._pastDays>0&&(e.has("_objects")||e.has("_pastDays"))&&this._loadPastHistories(),e.has("hass")&&this.hass){if(!this._dataLoaded)this._dataLoaded=!0,this._lastConnection=this.hass.connection,this._loadData(),this._subscribe();else if(this.hass.connection!==this._lastConnection){if(this._lastConnection=this.hass.connection,this._unsub){try{this._unsub()}catch{}this._unsub=null}this._subscribe(),this._loadData()}}}async _loadData(){try{let[e,t]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"}),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/statistics"})]);this._objects=e.objects,this._stats=t,Gt(this._stats.budget)}catch{}}async _loadPastHistories(){let e=as(this._objects,Ie(),this._pastDays||30),t=e.map(o=>o.sig).join("|");if(t===this._pastHistorySig)return;this._pastHistorySig=t;let i=++this._pastSeq,a=await Promise.all(e.map(async o=>{try{let d=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/history",entry_id:o.entryId,task_id:o.taskId});return[o.key,d.history??[]]}catch{return null}}));i===this._pastSeq&&(this._pastHistory=Object.fromEntries(a.filter(o=>o!==null)))}async _subscribe(){try{let e=await this.hass.connection.subscribeMessage(t=>{let i=t;this._objects=i.objects},{type:"maintenance_supporter/subscribe"});if(!this.isConnected){e();return}this._unsub=e}catch{}}_onEventClick(e){if(e.history_timestamp){this._openHistoryEntry(e);return}this._openTask(e)}_openTask(e){fr(e.entry_id,e.task_id)||this.dispatchEvent(new CustomEvent("ll-custom",{detail:{type:"maintenance-supporter:open-task",entry_id:e.entry_id,task_id:e.task_id},bubbles:!0,composed:!0}))}async _openHistoryEntry(e){try{let t=await se(this.hass);if(!xe(this.hass?.user,t.access)){this._openTask(e);return}}catch{}try{let t=await Hs(this.hass,e.entry_id,e.task_id,e.history_timestamp);if(!t||gr(t))return}catch{}this.dispatchEvent(new CustomEvent("ll-custom",{detail:{type:"maintenance-supporter:edit-history",entry_id:e.entry_id,task_id:e.task_id,original_timestamp:e.history_timestamp},bubbles:!0,composed:!0}))}render(){if(!this.hass)return h;let e=this._lang,t=this._config.show_window_chips!==!1,i=this._config.show_user_filter!==!1,a=this._config.title,o=null;this._userFilter&&(o=this._userFilter==="current_user"?this.hass?.user?.id??null:this._userFilter);let d=b=>{let le=b.toLowerCase();return this._objects.find(_e=>_e.entry_id===b||_e.object.name.toLowerCase()===le)?.entry_id??null},p=new Set(this._configuredObjects.map(d).filter(b=>b!==null)),_=p.size?this._objects.filter(b
Line truncated
|
||||
title="${n("cal_source_sensor",e)}" icon="mdi:trending-up"></ha-icon>`:l`<ha-icon class="cal-event-icon cal-source-time"
|
||||
title="${b.adaptive_enabled?n("cal_source_time_adaptive",e):n("cal_source_time",e)}"
|
||||
icon="${b.adaptive_enabled?"mdi:clock-time-four-outline":"mdi:clock-outline"}"></ha-icon>`,lt=z&&b.prediction_confidence&&b.status!=="triggered"&&!b.projected?l`<span class="cal-event-prediction cal-conf-${b.prediction_confidence}">
|
||||
${n("cal_predicted",e)} · ${n(`cal_confidence_${b.prediction_confidence}`,e)}
|
||||
</span>`:h,_i=He(this._stats?.budget),ha=b.history_type?n(b.history_type,e):n(b.status,e);return l`
|
||||
</span>`:h,mi=He(this._stats?.budget),fa=b.history_type?n(b.history_type,e):n(b.status,e);return l`
|
||||
<div class="cal-event ${Ee}"
|
||||
@click=${()=>this._onEventClick(b)}>
|
||||
${ot}
|
||||
<span class="cal-status-pill ${le}">${ha}</span>
|
||||
<span class="cal-status-pill ${le}">${fa}</span>
|
||||
<div class="cal-event-body">
|
||||
<div class="cal-event-title">${b.object_name} · ${b.task_name}${_e}</div>
|
||||
${lt}
|
||||
${H}
|
||||
</div>
|
||||
${b.avg_cost!=null&&b.avg_cost!==0?l`<span class="cal-event-cost">${J(b.avg_cost,_i,e)}</span>`:h}
|
||||
${b.avg_cost!=null&&b.avg_cost!==0?l`<span class="cal-event-cost">${J(b.avg_cost,mi,e)}</span>`:h}
|
||||
</div>
|
||||
`},W=b=>{let[le,Ee,_e]=b.date.split("-").map(Number),H=new Date(le,Ee-1,_e),z=b.date===w,ot=ws(H,e,"short"),lt=ks(H,e,"long");return l`
|
||||
`},W=b=>{let[le,Ee,_e]=b.date.split("-").map(Number),H=new Date(le,Ee-1,_e),z=b.date===w,ot=ks(H,e,"short"),lt=$s(H,e,"long");return l`
|
||||
<div class="cal-day-row">
|
||||
<div class="cal-day-pill ${z?"cal-today":""}">
|
||||
<span class="cal-pill-weekday">${ot}</span>
|
||||
@@ -5154,10 +5193,10 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
${M.length===0&&L?l`<div class="cal-empty">${n("cal_no_events",e)}</div>`:M.map(W)}
|
||||
</div>
|
||||
</ha-card>
|
||||
`}};G.styles=[Zt,ls,E`
|
||||
`}};G.styles=[Jt,cs,E`
|
||||
:host { display: block; }
|
||||
ha-card { padding: 0; overflow: hidden; }
|
||||
`],c([v({attribute:!1})],G.prototype,"hass",2),c([u()],G.prototype,"_config",2),c([u()],G.prototype,"_objects",2),c([u()],G.prototype,"_stats",2),c([u()],G.prototype,"_windowDays",2),c([u()],G.prototype,"_pastDays",2),c([u()],G.prototype,"_userFilter",2),c([u()],G.prototype,"_objectFilter",2),c([u()],G.prototype,"_unsub",2),c([u()],G.prototype,"_pastHistory",2);var hl=[{value:7,key:"cal_editor_window_week"},{value:14,key:"cal_editor_window_fortnight"},{value:30,key:"cal_editor_window_month"},{value:365,key:"cal_editor_window_year"}],_l=[{value:30,key:"cal_editor_window_past_30"},{value:90,key:"cal_editor_window_past_90"}],at=class extends S{constructor(){super(...arguments);this._config={type:"custom:maintenance-supporter-calendar-card"}}get _lang(){return N(this.hass)}setConfig(e){this._config={...e}}updated(){let e=this._lang;e&&!Le(e)&&Ne(e).then(()=>this.requestUpdate())}_valueChanged(e,t){let i={...this._config,[e]:t};e==="show_window_chips"&&t===!0&&delete i.show_window_chips,e==="show_user_filter"&&t===!0&&delete i.show_user_filter,e==="show_object_filter"&&t===!0&&delete i.show_object_filter,e==="title"&&(!t||typeof t=="string"&&t.trim()==="")&&delete i.title,e==="user_filter"&&t===""&&delete i.user_filter,this._emit(i)}_windowChanged(e){let t={...this._config};e.startsWith("past-")?(t.past_days=Number(e.slice(5)),delete t.window_days):(t.window_days=Number(e),delete t.past_days),this._emit(t)}_emit(e){this._config=e,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:e},bubbles:!0,composed:!0}))}render(){let e=this._lang,t=this._config.past_days?`past-${this._config.past_days}`:`${this._config.window_days??30}`,i=this._config.show_window_chips!==!1,a=this._config.show_user_filter!==!1,o=this._config.user_filter??"",d=this._config.title??"";return l`
|
||||
`],c([v({attribute:!1})],G.prototype,"hass",2),c([u()],G.prototype,"_config",2),c([u()],G.prototype,"_objects",2),c([u()],G.prototype,"_stats",2),c([u()],G.prototype,"_windowDays",2),c([u()],G.prototype,"_pastDays",2),c([u()],G.prototype,"_userFilter",2),c([u()],G.prototype,"_objectFilter",2),c([u()],G.prototype,"_unsub",2),c([u()],G.prototype,"_pastHistory",2);var fl=[{value:7,key:"cal_editor_window_week"},{value:14,key:"cal_editor_window_fortnight"},{value:30,key:"cal_editor_window_month"},{value:365,key:"cal_editor_window_year"}],vl=[{value:30,key:"cal_editor_window_past_30"},{value:90,key:"cal_editor_window_past_90"}],at=class extends S{constructor(){super(...arguments);this._config={type:"custom:maintenance-supporter-calendar-card"}}get _lang(){return N(this.hass)}setConfig(e){this._config={...e}}updated(){let e=this._lang;e&&!Le(e)&&Ne(e).then(()=>this.requestUpdate())}_valueChanged(e,t){let i={...this._config,[e]:t};e==="show_window_chips"&&t===!0&&delete i.show_window_chips,e==="show_user_filter"&&t===!0&&delete i.show_user_filter,e==="show_object_filter"&&t===!0&&delete i.show_object_filter,e==="title"&&(!t||typeof t=="string"&&t.trim()==="")&&delete i.title,e==="user_filter"&&t===""&&delete i.user_filter,this._emit(i)}_windowChanged(e){let t={...this._config};e.startsWith("past-")?(t.past_days=Number(e.slice(5)),delete t.window_days):(t.window_days=Number(e),delete t.past_days),this._emit(t)}_emit(e){this._config=e,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:e},bubbles:!0,composed:!0}))}render(){let e=this._lang,t=this._config.past_days?`past-${this._config.past_days}`:`${this._config.window_days??30}`,i=this._config.show_window_chips!==!1,a=this._config.show_user_filter!==!1,o=this._config.user_filter??"",d=this._config.title??"";return l`
|
||||
<div class="editor">
|
||||
<div class="row">
|
||||
<label for="title">${n("card_title",e)}</label>
|
||||
@@ -5174,8 +5213,8 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
id="window"
|
||||
@change=${p=>this._windowChanged(p.target.value)}
|
||||
>
|
||||
${hl.map(p=>l`<option value="${p.value}" ?selected=${`${p.value}`===t}>${n(p.key,e)}</option>`)}
|
||||
${_l.map(p=>l`<option value="past-${p.value}" ?selected=${`past-${p.value}`===t}>${n(p.key,e)}</option>`)}
|
||||
${fl.map(p=>l`<option value="${p.value}" ?selected=${`${p.value}`===t}>${n(p.key,e)}</option>`)}
|
||||
${vl.map(p=>l`<option value="past-${p.value}" ?selected=${`past-${p.value}`===t}>${n(p.key,e)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
<div class="row toggle">
|
||||
@@ -5244,7 +5283,7 @@ ${p?`<div class="sub">${p}</div>`:""}
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color, #666);
|
||||
}
|
||||
`,c([v({attribute:!1})],at.prototype,"hass",2),c([u()],at.prototype,"_config",2);customElements.get("maintenance-supporter-calendar-card")||customElements.define("maintenance-supporter-calendar-card",G);customElements.get("maintenance-supporter-calendar-card-editor")||customElements.define("maintenance-supporter-calendar-card-editor",at);Ts(["maintenance-supporter-calendar-card"],import.meta.url);Ss({type:"maintenance-supporter-calendar-card",name:"Maintenance Supporter \u2014 Calendar",description:"Rolling calendar of maintenance tasks with 7/14/30/365 day windows, source icons, and prediction-confidence pills.",preview:!0});export{G as MaintenanceCalendarCard};
|
||||
`,c([v({attribute:!1})],at.prototype,"hass",2),c([u()],at.prototype,"_config",2);customElements.get("maintenance-supporter-calendar-card")||customElements.define("maintenance-supporter-calendar-card",G);customElements.get("maintenance-supporter-calendar-card-editor")||customElements.define("maintenance-supporter-calendar-card-editor",at);As(["maintenance-supporter-calendar-card"],import.meta.url);Ts({type:"maintenance-supporter-calendar-card",name:"Maintenance Supporter \u2014 Calendar",description:"Rolling calendar of maintenance tasks with 7/14/30/365 day windows, source icons, and prediction-confidence pills.",preview:!0});export{G as MaintenanceCalendarCard};
|
||||
/*! Bundled license information:
|
||||
|
||||
@lit/reactive-element/css-tag.js:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*! maintenance_supporter frontend 2.98.0 */
|
||||
var Hs=Object.defineProperty;var $a=Object.getOwnPropertyDescriptor;var y=(r,s,e)=>()=>{if(e)throw e[0];try{return r&&(s=r(r=0)),s}catch(t){throw e=[t],t}};var Ea=(r,s)=>{for(var e in s)Hs(r,e,{get:s[e],enumerable:!0})};var c=(r,s,e,t)=>{for(var i=t>1?void 0:t?$a(s,e):s,a=r.length-1,o;a>=0;a--)(o=r[a])&&(i=(t?o(s,e,i):o(i))||i);return t&&i&&Hs(s,e,i),i};var Dt,Mt,$i,qs,pt,Ds,$,Ms,Ei,Si=y(()=>{Dt=globalThis,Mt=Dt.ShadowRoot&&(Dt.ShadyCSS===void 0||Dt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,$i=Symbol(),qs=new WeakMap,pt=class{constructor(s,e,t){if(this._$cssResult$=!0,t!==$i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=s,this.t=e}get styleSheet(){let s=this.o,e=this.t;if(Mt&&s===void 0){let t=e!==void 0&&e.length===1;t&&(s=qs.get(e)),s===void 0&&((this.o=s=new CSSStyleSheet).replaceSync(this.cssText),t&&qs.set(e,s))}return s}toString(){return this.cssText}},Ds=r=>new pt(typeof r=="string"?r:r+"",void 0,$i),$=(r,...s)=>{let e=r.length===1?r[0]:s.reduce((t,i,a)=>t+(o=>{if(o._$cssResult$===!0)return o.cssText;if(typeof o=="number")return o;throw Error("Value passed to 'css' function must be a 'css' function result: "+o+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+r[a+1],r[0]);return new pt(e,r,$i)},Ms=(r,s)=>{if(Mt)r.adoptedStyleSheets=s.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let e of s){let t=document.createElement("style"),i=Dt.litNonce;i!==void 0&&t.setAttribute("nonce",i),t.textContent=e.cssText,r.appendChild(t)}},Ei=Mt?r=>r:r=>r instanceof CSSStyleSheet?(s=>{let e="";for(let t of s.cssRules)e+=t.cssText;return Ds(e)})(r):r});var Sa,Ta,Aa,Ca,Ra,Ia,zt,zs,Pa,La,ut,ht,Ft,Fs,de,_t=y(()=>{Si();Si();({is:Sa,defineProperty:Ta,getOwnPropertyDescriptor:Aa,getOwnPropertyNames:Ca,getOwnPropertySymbols:Ra,getPrototypeOf:Ia}=Object),zt=globalThis,zs=zt.trustedTypes,Pa=zs?zs.emptyScript:"",La=zt.reactiveElementPolyfillSupport,ut=(r,s)=>r,ht={toAttribute(r,s){switch(s){case Boolean:r=r?Pa:null;break;case Object:case Array:r=r==null?r:JSON.stringify(r)}return r},fromAttribute(r,s){let e=r;switch(s){case Boolean:e=r!==null;break;case Number:e=r===null?null:Number(r);break;case Object:case Array:try{e=JSON.parse(r)}catch{e=null}}return e}},Ft=(r,s)=>!Sa(r,s),Fs={attribute:!0,type:String,converter:ht,reflect:!1,useDefault:!1,hasChanged:Ft};Symbol.metadata??=Symbol("metadata"),zt.litPropertyMetadata??=new WeakMap;de=class extends HTMLElement{static addInitializer(s){this._$Ei(),(this.l??=[]).push(s)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(s,e=Fs){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(s)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(s,e),!e.noAccessor){let t=Symbol(),i=this.getPropertyDescriptor(s,t,e);i!==void 0&&Ta(this.prototype,s,i)}}static getPropertyDescriptor(s,e,t){let{get:i,set:a}=Aa(this.prototype,s)??{get(){return this[e]},set(o){this[e]=o}};return{get:i,set(o){let d=i?.call(this);a?.call(this,o),this.requestUpdate(s,d,t)},configurable:!0,enumerable:!0}}static getPropertyOptions(s){return this.elementProperties.get(s)??Fs}static _$Ei(){if(this.hasOwnProperty(ut("elementProperties")))return;let s=Ia(this);s.finalize(),s.l!==void 0&&(this.l=[...s.l]),this.elementProperties=new Map(s.elementProperties)}static finalize(){if(this.hasOwnProperty(ut("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(ut("properties"))){let e=this.properties,t=[...Ca(e),...Ra(e)];for(let i of t)this.createProperty(i,e[i])}let s=this[Symbol.metadata];if(s!==null){let e=litPropertyMetadata.get(s);if(e!==void 0)for(let[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let i=this._$Eu(e,t);i!==void 0&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(s){let e=[];if(Array.isArray(s)){let t=new Set(s.flat(1/0).reverse());for(let i of t)e.unshift(Ei(i))}else s!==void 0&&e.push(Ei(s));return e}static _$Eu(s,e){let t=e.attribute;return t===!1?void 0:typeof t=="string"?t:typeof s=="string"?s.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(s=>this.enableUpdating=s),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(s=>s(this))}addController(s){(this._$EO??=new Set).add(s),this.renderRoot!==void 0&&this.isConnected&&s.hostConnected?.()}removeController(s){this._$EO?.delete(s)}_$E_(){let s=new Map,e=this.constructor.elementProperties;for(let t of e.keys())this.hasOwnProperty(t)&&(s.set(t,this[t]),delete this[t]);s.size>0&&(this._$Ep=s)}createRenderRoot(){let s=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return Ms(s,this.constructor.elementStyles),s}connectedCallback(){this.renderRoot??=tLine truncated
|
||||
\f\r]`,mt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Ws=/-->/g,Vs=/>/g,Te=RegExp(`>|${Ti}(?:([^\\s"'>=/]+)(${Ti}*=${Ti}*(?:[^
|
||||
\f\r"'\`<>=]|("|')|))|$)`,"g"),Gs=/'/g,Ks=/"/g,Js=/^(?:script|style|textarea|title)$/i,Oi=r=>(s,...e)=>({_$litType$:r,strings:s,values:e}),l=Oi(1),Ue=Oi(2),Nl=Oi(3),Re=Symbol.for("lit-noChange"),p=Symbol.for("lit-nothing"),Ys=new WeakMap,Ae=Ce.createTreeWalker(Ce,129);ja=(r,s)=>{let e=r.length-1,t=[],i,a=s===2?"<svg>":s===3?"<math>":"",o=mt;for(let d=0;d<e;d++){let h=r[d],_,m,b=-1,k=0;for(;k<h.length&&(o.lastIndex=k,m=o.exec(h),m!==null);)k=o.lastIndex,o===mt?m[1]==="!--"?o=Ws:m[1]!==void 0?o=Vs:m[2]!==void 0?(Js.test(m[2])&&(i=RegExp("</"+m[2],"g")),o=Te):m[3]!==void 0&&(o=Te):o===Te?m[0]===">"?(o=i??mt,b=-1):m[1]===void 0?b=-2:(b=o.lastIndex-m[2].length,_=m[1],o=m[3]===void 0?Te:m[3]==='"'?Ks:Gs):o===Ks||o===Gs?o=Te:o===Ws||o===Vs?o=mt:(o=Te,i=void 0);let w=o===Te&&r[d+1].startsWith("/>")?" ":"";a+=o===mt?h+Na:b>=0?(t.push(_),h.slice(0,b)+Qs+h.slice(b)+fe+w):h+fe+(b===-2?d:w)}return[Xs(r,a+(r[e]||"<?>")+(s===2?"</svg>":s===3?"</math>":"")),t]},vt=class r{constructor({strings:s,_$litType$:e},t){let i;this.parts=[];let a=0,o=0,d=s.length-1,h=this.parts,[_,m]=ja(s,e);if(this.el=r.createElement(_,t),Ae.currentNode=this.el.content,e===2||e===3){let b=this.el.content.firstChild;b.replaceWith(...b.childNodes)}for(;(i=Ae.nextNode())!==null&&h.length<d;){if(i.nodeType===1){if(i.hasAttributes())for(let b of i.getAttributeNames())if(b.endsWith(Qs)){let k=m[o++],w=i.getAttribute(b).split(fe),v=/([.?@])?(.*)/.exec(k);h.push({type:1,index:a,name:v[2],strings:w,ctor:v[1]==="."?Ci:v[1]==="?"?Ri:v[1]==="@"?Ii:Fe}),i.removeAttribute(b)}else b.startsWith(fe)&&(h.push({type:6,index:a}),i.removeAttribute(b));if(Js.test(i.tagName)){let b=i.textContent.split(fe),k=b.length-1;if(k>0){i.textContent=Ut?Ut.emptyScript:"";for(let w=0;w<k;w++)i.append(b[w],gt()),Ae.nextNode(),h.push({type:2,index:++a});i.append(b[k],gt())}}}else if(i.nodeType===8)if(i.data===Zs)h.push({type:2,index:a});else{let b=-1;for(;(b=i.data.indexOf(fe,b+1))!==-1;)h.push({type:7,index:a}),b+=fe.length-1}a++}}static createElement(s,e){let t=Ce.createElement("template");return t.innerHTML=s,t}};Ai=class{constructor(s,e){this._$AV=[],this._$AN=void 0,this._$AD=s,this._$AM=e}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(s){let{el:{content:e},parts:t}=this._$AD,i=(s?.creationScope??Ce).importNode(e,!0);Ae.currentNode=i;let a=Ae.nextNode(),o=0,d=0,h=t[0];for(;h!==void 0;){if(o===h.index){let _;h.type===2?_=new bt(a,a.nextSibling,this,s):h.type===1?_=new h.ctor(a,h.name,h.strings,this,s):h.type===6&&(_=new Pi(a,this,s)),this._$AV.push(_),h=t[++d]}o!==h?.index&&(a=Ae.nextNode(),o++)}return Ae.currentNode=Ce,i}p(s){let e=0;for(let t of this._$AV)t!==void 0&&(t.strings!==void 0?(t._$AI(s,t,e),e+=t.strings.length-2):t._$AI(s[e])),e++}},bt=class r{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(s,e,t,i){this.type=2,this._$AH=p,this._$AN=void 0,this._$AA=s,this._$AB=e,this._$AM=t,this.options=i,this._$Cv=i?.isConnected??!0}get parentNode(){let s=this._$AA.parentNode,e=this._$AM;return e!==void 0&&s?.nodeType===11&&(s=e.parentNode),s}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(s,e=this){s=ze(this,s,e),ft(s)?s===p||s==null||s===""?(this._$AH!==p&&this._$AR(),this._$AH=p):s!==this._$AH&&s!==Re&&this._(s):s._$litType$!==void 0?this.$(s):s.nodeType!==void 0?this.T(s):Oa(s)?this.k(s):this._(s)}O(s){return this._$AA.parentNode.insertBefore(s,this._$AB)}T(s){this._$AH!==s&&(this._$AR(),this._$AH=this.O(s))}_(s){this._$AH!==p&&ft(this._$AH)?this._$AA.nextSibling.data=s:this.T(Ce.createTextNode(s)),this._$AH=s}$(s){let{values:e,_$litType$:t}=s,i=typeof t=="number"?this._$AC(s):(t.el===void 0&&(t.el=vt.createElement(Xs(t.h,t.h[0]),this.options)),t);if(this._$AH?._$AD===i)this._$AH.p(e);else{let a=new Ai(i,this),o=a.u(this.options);a.p(e),this.T(o),this._$AH=a}}_$AC(s){let e=Ys.get(s.strings);return e===void 0&&Ys.set(s.strings,e=new vt(s)),e}k(s){Ni(this._$AH)||(this._$AH=[],this._$AR());let e=this._$AH,t,i=0;for(let a of s)i===e.length?e.push(t=new r(this.O(gt()),this.O(gt()),this,this.options)):t=e[i],t._$AI(a),i++;i<e.length&&(this._$AR(t&&t._$AB.nextSibling,i),e.length=i)}_$AR(s=this._$AA.nextSibling,e){for(this._$AP?.(!1,!0,e);s!==this._$AB;){let t=Us(s).nextSibling;Us(s).remove(),s=t}}setConnected(s){this._$AM===void 0&&(this._$Cv=s,this._$AP?.(s))}},Fe=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(s,e,t,i,a){this.type=1,this._$AH=p,this._$AN=void 0,this.element=s,this.name=e,this._$AM=i,this.options=a,t.length>2||t[0]!==""||t[1]!==""?(this._$AH=Array(t.length-1).fill(new String),this.strings=t):this._$AH=p}_$AI(s,e=this,t,i){let a=this.strings,o=!1;if(a===void 0)s=ze(this,s,e,0),o=!ft(s)||s!==this._$AH&&s!==Re,o&&(this._$AH=s);else{let d=s,h,_;for(s=a[0],h=0;h<a.length-1;h++)_=ze(this,d[t+h],e,h),_===Re&&(_=this._$AH[h]),o||=!ft(_)||_!==this._$AH[h],_===p?s=p:s!==p&&(s+=(_??"")+a[h+1]),this._$AH[h]=_}o&&!i&&this.j(s)}j(s){s===p?this.element.removeAttribute(this.naLine truncated
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
var qs=Object.defineProperty;var Aa=Object.getOwnPropertyDescriptor;var y=(r,s,e)=>()=>{if(e)throw e[0];try{return r&&(s=r(r=0)),s}catch(t){throw e=[t],t}};var Ca=(r,s)=>{for(var e in s)qs(r,e,{get:s[e],enumerable:!0})};var c=(r,s,e,t)=>{for(var i=t>1?void 0:t?Aa(s,e):s,a=r.length-1,o;a>=0;a--)(o=r[a])&&(i=(t?o(s,e,i):o(i))||i);return t&&i&&qs(s,e,i),i};var Mt,zt,Ei,Ds,pt,Ms,$,zs,Si,Ti=y(()=>{Mt=globalThis,zt=Mt.ShadowRoot&&(Mt.ShadyCSS===void 0||Mt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Ei=Symbol(),Ds=new WeakMap,pt=class{constructor(s,e,t){if(this._$cssResult$=!0,t!==Ei)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=s,this.t=e}get styleSheet(){let s=this.o,e=this.t;if(zt&&s===void 0){let t=e!==void 0&&e.length===1;t&&(s=Ds.get(e)),s===void 0&&((this.o=s=new CSSStyleSheet).replaceSync(this.cssText),t&&Ds.set(e,s))}return s}toString(){return this.cssText}},Ms=r=>new pt(typeof r=="string"?r:r+"",void 0,Ei),$=(r,...s)=>{let e=r.length===1?r[0]:s.reduce((t,i,a)=>t+(o=>{if(o._$cssResult$===!0)return o.cssText;if(typeof o=="number")return o;throw Error("Value passed to 'css' function must be a 'css' function result: "+o+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+r[a+1],r[0]);return new pt(e,r,Ei)},zs=(r,s)=>{if(zt)r.adoptedStyleSheets=s.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet);else for(let e of s){let t=document.createElement("style"),i=Mt.litNonce;i!==void 0&&t.setAttribute("nonce",i),t.textContent=e.cssText,r.appendChild(t)}},Si=zt?r=>r:r=>r instanceof CSSStyleSheet?(s=>{let e="";for(let t of s.cssRules)e+=t.cssText;return Ms(e)})(r):r});var Ra,Ia,Pa,La,Na,Oa,Ft,Fs,Ha,ja,ut,ht,Ut,Us,de,_t=y(()=>{Ti();Ti();({is:Ra,defineProperty:Ia,getOwnPropertyDescriptor:Pa,getOwnPropertyNames:La,getOwnPropertySymbols:Na,getPrototypeOf:Oa}=Object),Ft=globalThis,Fs=Ft.trustedTypes,Ha=Fs?Fs.emptyScript:"",ja=Ft.reactiveElementPolyfillSupport,ut=(r,s)=>r,ht={toAttribute(r,s){switch(s){case Boolean:r=r?Ha:null;break;case Object:case Array:r=r==null?r:JSON.stringify(r)}return r},fromAttribute(r,s){let e=r;switch(s){case Boolean:e=r!==null;break;case Number:e=r===null?null:Number(r);break;case Object:case Array:try{e=JSON.parse(r)}catch{e=null}}return e}},Ut=(r,s)=>!Ra(r,s),Us={attribute:!0,type:String,converter:ht,reflect:!1,useDefault:!1,hasChanged:Ut};Symbol.metadata??=Symbol("metadata"),Ft.litPropertyMetadata??=new WeakMap;de=class extends HTMLElement{static addInitializer(s){this._$Ei(),(this.l??=[]).push(s)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(s,e=Us){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(s)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(s,e),!e.noAccessor){let t=Symbol(),i=this.getPropertyDescriptor(s,t,e);i!==void 0&&Ia(this.prototype,s,i)}}static getPropertyDescriptor(s,e,t){let{get:i,set:a}=Pa(this.prototype,s)??{get(){return this[e]},set(o){this[e]=o}};return{get:i,set(o){let d=i?.call(this);a?.call(this,o),this.requestUpdate(s,d,t)},configurable:!0,enumerable:!0}}static getPropertyOptions(s){return this.elementProperties.get(s)??Us}static _$Ei(){if(this.hasOwnProperty(ut("elementProperties")))return;let s=Oa(this);s.finalize(),s.l!==void 0&&(this.l=[...s.l]),this.elementProperties=new Map(s.elementProperties)}static finalize(){if(this.hasOwnProperty(ut("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(ut("properties"))){let e=this.properties,t=[...La(e),...Na(e)];for(let i of t)this.createProperty(i,e[i])}let s=this[Symbol.metadata];if(s!==null){let e=litPropertyMetadata.get(s);if(e!==void 0)for(let[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(let[e,t]of this.elementProperties){let i=this._$Eu(e,t);i!==void 0&&this._$Eh.set(i,e)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(s){let e=[];if(Array.isArray(s)){let t=new Set(s.flat(1/0).reverse());for(let i of t)e.unshift(Si(i))}else s!==void 0&&e.push(Si(s));return e}static _$Eu(s,e){let t=e.attribute;return t===!1?void 0:typeof t=="string"?t:typeof s=="string"?s.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(s=>this.enableUpdating=s),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(s=>s(this))}addController(s){(this._$EO??=new Set).add(s),this.renderRoot!==void 0&&this.isConnected&&s.hostConnected?.()}removeController(s){this._$EO?.delete(s)}_$E_(){let s=new Map,e=this.constructor.elementProperties;for(let t of e.keys())this.hasOwnProperty(t)&&(s.set(t,this[t]),delete this[t]);s.size>0&&(this._$Ep=s)}createRenderRoot(){let s=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return zs(s,this.constructor.elementStyles),s}connectedCallback(){this.renderRoot??=tLine truncated
|
||||
\f\r]`,mt=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Vs=/-->/g,Gs=/>/g,Te=RegExp(`>|${Ai}(?:([^\\s"'>=/]+)(${Ai}*=${Ai}*(?:[^
|
||||
\f\r"'\`<>=]|("|')|))|$)`,"g"),Ks=/'/g,Ys=/"/g,Xs=/^(?:script|style|textarea|title)$/i,Hi=r=>(s,...e)=>({_$litType$:r,strings:s,values:e}),l=Hi(1),Ue=Hi(2),ql=Hi(3),Re=Symbol.for("lit-noChange"),p=Symbol.for("lit-nothing"),Qs=new WeakMap,Ae=Ce.createTreeWalker(Ce,129);Ma=(r,s)=>{let e=r.length-1,t=[],i,a=s===2?"<svg>":s===3?"<math>":"",o=mt;for(let d=0;d<e;d++){let h=r[d],_,m,b=-1,k=0;for(;k<h.length&&(o.lastIndex=k,m=o.exec(h),m!==null);)k=o.lastIndex,o===mt?m[1]==="!--"?o=Vs:m[1]!==void 0?o=Gs:m[2]!==void 0?(Xs.test(m[2])&&(i=RegExp("</"+m[2],"g")),o=Te):m[3]!==void 0&&(o=Te):o===Te?m[0]===">"?(o=i??mt,b=-1):m[1]===void 0?b=-2:(b=o.lastIndex-m[2].length,_=m[1],o=m[3]===void 0?Te:m[3]==='"'?Ys:Ks):o===Ys||o===Ks?o=Te:o===Vs||o===Gs?o=mt:(o=Te,i=void 0);let w=o===Te&&r[d+1].startsWith("/>")?" ":"";a+=o===mt?h+qa:b>=0?(t.push(_),h.slice(0,b)+Zs+h.slice(b)+fe+w):h+fe+(b===-2?d:w)}return[er(r,a+(r[e]||"<?>")+(s===2?"</svg>":s===3?"</math>":"")),t]},vt=class r{constructor({strings:s,_$litType$:e},t){let i;this.parts=[];let a=0,o=0,d=s.length-1,h=this.parts,[_,m]=Ma(s,e);if(this.el=r.createElement(_,t),Ae.currentNode=this.el.content,e===2||e===3){let b=this.el.content.firstChild;b.replaceWith(...b.childNodes)}for(;(i=Ae.nextNode())!==null&&h.length<d;){if(i.nodeType===1){if(i.hasAttributes())for(let b of i.getAttributeNames())if(b.endsWith(Zs)){let k=m[o++],w=i.getAttribute(b).split(fe),v=/([.?@])?(.*)/.exec(k);h.push({type:1,index:a,name:v[2],strings:w,ctor:v[1]==="."?Ri:v[1]==="?"?Ii:v[1]==="@"?Pi:Fe}),i.removeAttribute(b)}else b.startsWith(fe)&&(h.push({type:6,index:a}),i.removeAttribute(b));if(Xs.test(i.tagName)){let b=i.textContent.split(fe),k=b.length-1;if(k>0){i.textContent=Bt?Bt.emptyScript:"";for(let w=0;w<k;w++)i.append(b[w],gt()),Ae.nextNode(),h.push({type:2,index:++a});i.append(b[k],gt())}}}else if(i.nodeType===8)if(i.data===Js)h.push({type:2,index:a});else{let b=-1;for(;(b=i.data.indexOf(fe,b+1))!==-1;)h.push({type:7,index:a}),b+=fe.length-1}a++}}static createElement(s,e){let t=Ce.createElement("template");return t.innerHTML=s,t}};Ci=class{constructor(s,e){this._$AV=[],this._$AN=void 0,this._$AD=s,this._$AM=e}get parentNode(){return this._$AM.parentNode}get _$AU(){return this._$AM._$AU}u(s){let{el:{content:e},parts:t}=this._$AD,i=(s?.creationScope??Ce).importNode(e,!0);Ae.currentNode=i;let a=Ae.nextNode(),o=0,d=0,h=t[0];for(;h!==void 0;){if(o===h.index){let _;h.type===2?_=new bt(a,a.nextSibling,this,s):h.type===1?_=new h.ctor(a,h.name,h.strings,this,s):h.type===6&&(_=new Li(a,this,s)),this._$AV.push(_),h=t[++d]}o!==h?.index&&(a=Ae.nextNode(),o++)}return Ae.currentNode=Ce,i}p(s){let e=0;for(let t of this._$AV)t!==void 0&&(t.strings!==void 0?(t._$AI(s,t,e),e+=t.strings.length-2):t._$AI(s[e])),e++}},bt=class r{get _$AU(){return this._$AM?._$AU??this._$Cv}constructor(s,e,t,i){this.type=2,this._$AH=p,this._$AN=void 0,this._$AA=s,this._$AB=e,this._$AM=t,this.options=i,this._$Cv=i?.isConnected??!0}get parentNode(){let s=this._$AA.parentNode,e=this._$AM;return e!==void 0&&s?.nodeType===11&&(s=e.parentNode),s}get startNode(){return this._$AA}get endNode(){return this._$AB}_$AI(s,e=this){s=ze(this,s,e),ft(s)?s===p||s==null||s===""?(this._$AH!==p&&this._$AR(),this._$AH=p):s!==this._$AH&&s!==Re&&this._(s):s._$litType$!==void 0?this.$(s):s.nodeType!==void 0?this.T(s):Da(s)?this.k(s):this._(s)}O(s){return this._$AA.parentNode.insertBefore(s,this._$AB)}T(s){this._$AH!==s&&(this._$AR(),this._$AH=this.O(s))}_(s){this._$AH!==p&&ft(this._$AH)?this._$AA.nextSibling.data=s:this.T(Ce.createTextNode(s)),this._$AH=s}$(s){let{values:e,_$litType$:t}=s,i=typeof t=="number"?this._$AC(s):(t.el===void 0&&(t.el=vt.createElement(er(t.h,t.h[0]),this.options)),t);if(this._$AH?._$AD===i)this._$AH.p(e);else{let a=new Ci(i,this),o=a.u(this.options);a.p(e),this.T(o),this._$AH=a}}_$AC(s){let e=Qs.get(s.strings);return e===void 0&&Qs.set(s.strings,e=new vt(s)),e}k(s){Oi(this._$AH)||(this._$AH=[],this._$AR());let e=this._$AH,t,i=0;for(let a of s)i===e.length?e.push(t=new r(this.O(gt()),this.O(gt()),this,this.options)):t=e[i],t._$AI(a),i++;i<e.length&&(this._$AR(t&&t._$AB.nextSibling,i),e.length=i)}_$AR(s=this._$AA.nextSibling,e){for(this._$AP?.(!1,!0,e);s!==this._$AB;){let t=Bs(s).nextSibling;Bs(s).remove(),s=t}}setConnected(s){this._$AM===void 0&&(this._$Cv=s,this._$AP?.(s))}},Fe=class{get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}constructor(s,e,t,i,a){this.type=1,this._$AH=p,this._$AN=void 0,this.element=s,this.name=e,this._$AM=i,this.options=a,t.length>2||t[0]!==""||t[1]!==""?(this._$AH=Array(t.length-1).fill(new String),this.strings=t):this._$AH=p}_$AI(s,e=this,t,i){let a=this.strings,o=!1;if(a===void 0)s=ze(this,s,e,0),o=!ft(s)||s!==this._$AH&&s!==Re,o&&(this._$AH=s);else{let d=s,h,_;for(s=a[0],h=0;h<a.length-1;h++)_=ze(this,d[t+h],e,h),_===Re&&(_=this._$AH[h]),o||=!ft(_)||_!==this._$AH[h],_===p?s=p:s!==p&&(s+=(_??"")+a[h+1]),this._$AH[h]=_}o&&!i&&this.j(s)}j(s){s===p?this.element.removeAttribute(this.naLine 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",ws_err_not_found:"Not found \u2014 it may have been deleted meanwhile",ws_err_invalid_input:"Invalid input",ws_err_invalid_date:"Invalid date",ws_err_invalid_format:"Invalid format",ws_err_invalid_url:"Invalid link",ws_err_invalid_mirror_todo:"Invalid to-do list",ws_err_not_configured:"Not set up yet",ws_err_not_loaded:"Not loaded yet \u2014 try again in a moment",ws_err_limit_reached:"Limit reached",ws_err_too_many:"Too many entries",ws_err_too_large:"Too large",ws_err_not_archived:"Not archived",ws_err_invalid_target:"Invalid target",ws_err_create_failed:"Could not be created",ws_err_archived:"Archived \u2014 restore it first",ws_err_already_archived:"Already archived",ws_err_unavailable:"Currently unavailable",ws_err_unauthorized:"Not permitted for your user",ws_err_too_many_views:"Too many saved views",ws_err_storage_unavailable:"Storage is not available",ws_err_replace_failed:"Replacement failed",ws_err_not_paused:"Not paused",ws_err_not_available:"Nothing available",ws_err_no_url:"No link given",ws_err_no_phases:"This task has no phases",ws_err_invalid_view:"Invalid view",ws_err_invalid_user:"Unknown user",ws_err_invalid_range:"Invalid range",ws_err_invalid_parent:"Invalid parent object",ws_err_invalid_device:"Unknown device",ws_err_empty_csv:"The CSV file is empty",ws_err_empty:"Nothing to import",ws_err_duplicate_failed:"Could not duplicate",ws_err_already_paused:"Already paused",ws_err_invalid_cursor:"Invalid phase position",ws_err_invalid_entity_slug:"Invalid entity name",ws_err_invalid_trigger_config:"Invalid trigger configuration",ws_err_no_defaults:"No quick-complete defaults set",ws_err_self_link_device:"An object cannot be linked to itself",ws_err_too_early:"Too early \u2014 this task can only be completed closer to its due date",ws_err_invalid_search_template:"The search link must be an http(s) URL containing {q}",ws_err_invalid_icon:"The notification icon must be an mdi: name, e.g. mdi:air-filter",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_toggle:"Due at a specific time",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",notes_markdown_hint:"Markdown is supported \u2014 **bold**, lists, [links](\u2026)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",completed_at_optional:"Completed at (optional, empty = now)",completed_at_pick:"Set date & time",completed_at_future_error:"The completion date cannot be in the future.",ha_time_zone_hint:"Time in Home Assistant's time zone ({zone})",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",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.",task_mirror_todo:"Mirror into to-do lists",task_mirror_todo_hint:"The task appears in these lists while it is due; checking it off there completes it here.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",all_priorities:"All priorities",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",object_replaces:"Replaces",object_replaced_by:"Replaced by",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",all_parts:"All parts",all_areas:"All areas",areas_filter:"Filter areas\u2026",areas_filter_none:"No area matches the filter.",area_cost_year:"Cost {year}",area_object_count:"{n} o
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",ws_err_not_found:"Not found \u2014 it may have been deleted meanwhile",ws_err_invalid_input:"Invalid input",ws_err_invalid_date:"Invalid date",ws_err_invalid_format:"Invalid format",ws_err_invalid_url:"Invalid link",ws_err_invalid_mirror_todo:"Invalid to-do list",ws_err_not_configured:"Not set up yet",ws_err_not_loaded:"Not loaded yet \u2014 try again in a moment",ws_err_limit_reached:"Limit reached",ws_err_too_many:"Too many entries",ws_err_too_large:"Too large",ws_err_not_archived:"Not archived",ws_err_invalid_target:"Invalid target",ws_err_create_failed:"Could not be created",ws_err_archived:"Archived \u2014 restore it first",ws_err_already_archived:"Already archived",ws_err_unavailable:"Currently unavailable",ws_err_unauthorized:"Not permitted for your user",ws_err_too_many_views:"Too many saved views",ws_err_storage_unavailable:"Storage is not available",ws_err_replace_failed:"Replacement failed",ws_err_not_paused:"Not paused",ws_err_not_available:"Nothing available",ws_err_no_url:"No link given",ws_err_no_phases:"This task has no phases",ws_err_invalid_view:"Invalid view",ws_err_invalid_user:"Unknown user",ws_err_invalid_range:"Invalid range",ws_err_invalid_parent:"Invalid parent object",ws_err_invalid_device:"Unknown device",ws_err_empty_csv:"The CSV file is empty",ws_err_empty:"Nothing to import",ws_err_duplicate_failed:"Could not duplicate",ws_err_already_paused:"Already paused",ws_err_invalid_cursor:"Invalid phase position",ws_err_invalid_entity_slug:"Invalid entity name",ws_err_invalid_trigger_config:"Invalid trigger configuration",ws_err_no_defaults:"No quick-complete defaults set",ws_err_self_link_device:"An object cannot be linked to itself",ws_err_too_early:"Too early \u2014 this task can only be completed closer to its due date",ws_err_invalid_search_template:"The search link must be an http(s) URL containing {q}",ws_err_invalid_icon:"The notification icon must be an mdi: name, e.g. mdi:air-filter",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_toggle:"Due at a specific time",schedule_time_help:"Empty = midnight (default). HA timezone.",at_time:"at",notes_optional:"Notes (optional)",notes_markdown_hint:"Markdown is supported \u2014 **bold**, lists, [links](\u2026)",cost_optional:"Cost (optional)",duration_minutes:"Duration in minutes (optional)",completed_at_optional:"Completed at (optional, empty = now)",completed_at_pick:"Set date & time",completed_at_future_error:"The completion date cannot be in the future.",ha_time_zone_hint:"Time in Home Assistant's time zone ({zone})",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",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.",task_mirror_todo:"Mirror into to-do lists",task_mirror_todo_hint:"The task appears in these lists while it is due; checking it off there completes it here.",priority_low:"Low",priority_normal:"Normal",priority_high:"High",all_priorities:"All priorities",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",object_replaces:"Replaces",object_replaced_by:"Replaced by",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",all_parts:"All parts",all_areas:"All areas",areas_filter:"Filter areas\u2026",areas_filter_none:"No area matches the filter.",area_cost_year:"Cost {year}",area_object_count:"{n} o
Line truncated
|
||||
.field { display: flex; flex-direction: column; gap: 4px; }
|
||||
.field-label { font-size: 12px; color: var(--secondary-text-color); }
|
||||
.field-input {
|
||||
@@ -14,7 +14,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
font-family: inherit; width: 100%; box-sizing: border-box;
|
||||
}
|
||||
.field-input:focus { outline: none; border-color: var(--primary-color); }
|
||||
`,ao=$`
|
||||
`,po=$`
|
||||
.person-chip { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; vertical-align: middle; }
|
||||
.person-avatar {
|
||||
width: 22px; height: 22px; border-radius: 50%; flex: none;
|
||||
@@ -24,7 +24,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.person-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
||||
`,oo=$`
|
||||
`,uo=$`
|
||||
.ref-chip {
|
||||
display: inline-block; font-size: 11.5px; font-weight: 500; line-height: 1; padding: 3px 6px;
|
||||
border-radius: 6px; border: 1px solid var(--divider-color); color: var(--secondary-text-color);
|
||||
@@ -33,9 +33,9 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
}
|
||||
/* #189: "· Residual waste, Paper" after a calendar-driven task's name. */
|
||||
.event-titles { color: var(--secondary-text-color); font-weight: 400; }
|
||||
`,ti=$`
|
||||
${ao}
|
||||
${oo}
|
||||
`,ii=$`
|
||||
${po}
|
||||
${uo}
|
||||
:host {
|
||||
--maint-ok-color: var(--success-color, #4caf50);
|
||||
--maint-due-soon-color: var(--warning-color, #ff9800);
|
||||
@@ -1270,20 +1270,20 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.stat-item .stat-label { font-size: 11px; white-space: normal; text-align: center; line-height: 1.2; hyphens: auto; overflow-wrap: anywhere; }
|
||||
.stat-value { font-size: 20px; }
|
||||
}
|
||||
`});function jr(r,s,e){let t=new Blob([r],{type:e}),i=URL.createObjectURL(t),a=document.createElement("a");a.href=i,a.download=s,a.target="_blank",a.rel="noopener",a.style.display="none",document.body.appendChild(a),a.dispatchEvent(new MouseEvent("click")),document.body.removeChild(a),setTimeout(()=>URL.revokeObjectURL(i),6e4)}var Zi=y(()=>{"use strict"});async function lo(r,s,e=300){return(await r.connection.sendMessagePromise({type:"auth/sign_path",path:s,expires:e})).path}async function Ji(r,s,e=300){return lo(r,`/api/maintenance_supporter/document/${s}`,e)}async function Hr(r,s,e=""){let t=window.open("about:blank","_blank");try{let i=await Ji(r,s);t&&(t.location.href=new URL(i+e,window.location.origin).href)}catch(i){throw t&&t.close(),i}}var Xi=y(()=>{"use strict";Zi()});function ii(r){return!!r&&/^https?:\/\//i.test(r)}var es=y(()=>{"use strict"});function uo(r){let s=(r||"").split(/\s+/).filter(Boolean);return s.length===0?"?":s.length===1?s[0][0].toUpperCase():(s[0][0]+s[s.length-1][0]).toUpperCase()}function ho(r){let s=0;for(let e of r)s=s*31+e.charCodeAt(0)>>>0;return zr[s%zr.length]}function ri(r){return r?{id:r.id,name:r.name,initials:r.initials||uo(r.name),color:r.color||ho(r.id)}:null}function is(r){return r?l`<span class="person-avatar" style="--person-color: ${r.color}" title=${r.name}>${r.initials}</span>`:p}var zr,ss=y(()=>{"use strict";R();zr=["#c62828","#ad1457","#6a1b9a","#4527a0","#283593","#1565c0","#00838f","#2e7d32","#558b2f","#ef6c00","#6d4c41","#546e7a"]});var Qe,rs=y(()=>{"use strict";ss();Qe=class{constructor(s){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=s}updateHass(s){this.hass=s}async getUsers(s=!1){let e=Date.now();if(!s&&this.usersCache&&e-this.cacheTimestamp<this.CACHE_TTL_MS)return this.usersCache;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/users/list"});return this.usersCache=t.users,this.cacheTimestamp=e,this.usersCache}catch(t){return console.error("Failed to fetch users:",t),this.usersCache||[]}}async assignUser(s,e,t){await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/assign_user",entry_id:s,task_id:e,user_id:t})}async getTasksByUser(s){return(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tasks/by_user",user_id:s})).tasks}getUserName(s){return!s||!this.usersCache?null:this.usersCache.find(t=>t.id===s)?.name||null}getPerson(s){return ri(this.getUser(s))}getUser(s){return!s||!this.usersCache?null:this.usersCache.find(e=>e.id===s)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(s){return s?s===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}}});function _o(r,s){if(s<=0)return 0;let e=typeof r=="number"&&Number.isFinite(r)?Math.trunc(r):0;return e<0?0:e%s}function mo(r){return!!(r?.phases&&r.phase_sequence&&r.phase_sequence.length>0)}function ns(r){if(!r||!mo(r))return null;let s=r.phase_sequence,e=_o(r.phase_cursor,s.length),t=s[e],i=r.phases?.[t];return i?{id:t,name:i.name,index:e,count:s.length,notes:i.notes,checklist:i.checklist!==void 0?i.checklist:r.checklist??[],consumesParts:i.consumes_parts!==void 0?i.consumes_parts:r.consumes_parts??[],requiredFields:i.required_completion_fields!==void 0?i.required_completion_fields:r.required_completion_fields??[]}:null}function ye(r){let s=ns(r);return s?`${s.index+1}/${s.count} \xB7 ${s.name}`:""}var ni=y(()=>{"use strict"});function Ur(){let r=new Uint8Array(4);return(globalThis.crypto??{getRandomValues:s=>s.map(()=>Math.floor(Math.random()*256))}).getRandomValues(r),Array.from(r,s=>s.toString(16).padStart(2,"0")).join("")}function xe(r){let s=r?.reading_values;if(!Array.isArray(s))return[];let e=[],t=new Set;for(let i of s){if(!i||typeof i!="object")continue;let a=i;typeof a.id!="string"||!a.id||t.has(a.id)||typeof a.value!="number"||!Number.isFinite(a.value)||(t.add(a.id),e.push({id:a.id,name:typeof a.name=="string"?a.name:a.id,unit:typeof a.unit=="string"?a.unit:null,value:a.value}))}return e}function Br(r){return r.filter(s=>s.type==="completed"&&(s.reading_value!=null||xe(s).length>0)).sort((s,e)=>s.timestamp.localeCompare(e.timestamp))}function Wr(r){return Br(r??[]).map(s=>({timestamp:s.timestamp,values:xe(s)})).filter(s=>s.values.length>0)}function Vr(r,s,e){let t;for(let i of r){if(e!==void 0){let o=G(i.timestamp);if(!isNaN(o)&&o>=e)break}let a=i.values.find(o=>o.id===s);a&&(t=a)}return t}function Gr(r,s,e){let t=xe(s).find(a=>a.id===e);if(!t)return null;let i=null;for(let a of Br(r)){if(a.timestamp>=s.timestamp)break;let o=xe(a).find(d=>d.id===e);o&&(i=o.value)}return i==null?null:t.value-i}function Kr(r){let s=new Set,e=new Set;for(let t of r){let i=(t.name||"").trim().toLowerCase();i&&(s.has(i)?e.add(t.id):s.add(i))}return e}function Yr(r){let s=[],e=new Set,t=new Set;for(let i of r){let a=(i.name||"").trim();if(!(!a||e.has(i.id)||t.has(a.toLowerCase()))&&(e.add(i.id),t.add(a.toLowerCase()),s.push({id:i.id,name:a,unit:(i.unLine truncated
|
||||
`});function jr(r,s,e){let t=new Blob([r],{type:e}),i=URL.createObjectURL(t),a=document.createElement("a");a.href=i,a.download=s,a.target="_blank",a.rel="noopener",a.style.display="none",document.body.appendChild(a),a.dispatchEvent(new MouseEvent("click")),document.body.removeChild(a),setTimeout(()=>URL.revokeObjectURL(i),6e4)}var Ji=y(()=>{"use strict"});async function ho(r,s,e=300){return(await r.connection.sendMessagePromise({type:"auth/sign_path",path:s,expires:e})).path}async function Xi(r,s,e=300){return ho(r,`/api/maintenance_supporter/document/${s}`,e)}async function qr(r,s,e=""){let t=window.open("about:blank","_blank");try{let i=await Xi(r,s);t&&(t.location.href=new URL(i+e,window.location.origin).href)}catch(i){throw t&&t.close(),i}}var es=y(()=>{"use strict";Ji()});function si(r){return!!r&&/^https?:\/\//i.test(r)}var ts=y(()=>{"use strict"});function go(r){let s=(r||"").split(/\s+/).filter(Boolean);return s.length===0?"?":s.length===1?s[0][0].toUpperCase():(s[0][0]+s[s.length-1][0]).toUpperCase()}function fo(r){let s=0;for(let e of r)s=s*31+e.charCodeAt(0)>>>0;return Fr[s%Fr.length]}function ni(r){return r?{id:r.id,name:r.name,initials:r.initials||go(r.name),color:r.color||fo(r.id)}:null}function ss(r){return r?l`<span class="person-avatar" style="--person-color: ${r.color}" title=${r.name}>${r.initials}</span>`:p}var Fr,rs=y(()=>{"use strict";R();Fr=["#c62828","#ad1457","#6a1b9a","#4527a0","#283593","#1565c0","#00838f","#2e7d32","#558b2f","#ef6c00","#6d4c41","#546e7a"]});var Qe,ns=y(()=>{"use strict";rs();Qe=class{constructor(s){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=s}updateHass(s){this.hass=s}async getUsers(s=!1){let e=Date.now();if(!s&&this.usersCache&&e-this.cacheTimestamp<this.CACHE_TTL_MS)return this.usersCache;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/users/list"});return this.usersCache=t.users,this.cacheTimestamp=e,this.usersCache}catch(t){return console.error("Failed to fetch users:",t),this.usersCache||[]}}async assignUser(s,e,t){await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/assign_user",entry_id:s,task_id:e,user_id:t})}async getTasksByUser(s){return(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tasks/by_user",user_id:s})).tasks}getUserName(s){return!s||!this.usersCache?null:this.usersCache.find(t=>t.id===s)?.name||null}getPerson(s){return ni(this.getUser(s))}getUser(s){return!s||!this.usersCache?null:this.usersCache.find(e=>e.id===s)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(s){return s?s===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}}});function vo(r,s){if(s<=0)return 0;let e=typeof r=="number"&&Number.isFinite(r)?Math.trunc(r):0;return e<0?0:e%s}function bo(r){return!!(r?.phases&&r.phase_sequence&&r.phase_sequence.length>0)}function as(r){if(!r||!bo(r))return null;let s=r.phase_sequence,e=vo(r.phase_cursor,s.length),t=s[e],i=r.phases?.[t];return i?{id:t,name:i.name,index:e,count:s.length,notes:i.notes,checklist:i.checklist!==void 0?i.checklist:r.checklist??[],consumesParts:i.consumes_parts!==void 0?i.consumes_parts:r.consumes_parts??[],requiredFields:i.required_completion_fields!==void 0?i.required_completion_fields:r.required_completion_fields??[]}:null}function ye(r){let s=as(r);return s?`${s.index+1}/${s.count} \xB7 ${s.name}`:""}var ai=y(()=>{"use strict"});function Br(){let r=new Uint8Array(4);return(globalThis.crypto??{getRandomValues:s=>s.map(()=>Math.floor(Math.random()*256))}).getRandomValues(r),Array.from(r,s=>s.toString(16).padStart(2,"0")).join("")}function xe(r){let s=r?.reading_values;if(!Array.isArray(s))return[];let e=[],t=new Set;for(let i of s){if(!i||typeof i!="object")continue;let a=i;typeof a.id!="string"||!a.id||t.has(a.id)||typeof a.value!="number"||!Number.isFinite(a.value)||(t.add(a.id),e.push({id:a.id,name:typeof a.name=="string"?a.name:a.id,unit:typeof a.unit=="string"?a.unit:null,value:a.value}))}return e}function Wr(r){return r.filter(s=>s.type==="completed"&&(s.reading_value!=null||xe(s).length>0)).sort((s,e)=>s.timestamp.localeCompare(e.timestamp))}function Vr(r){return Wr(r??[]).map(s=>({timestamp:s.timestamp,values:xe(s)})).filter(s=>s.values.length>0)}function Gr(r,s,e){let t;for(let i of r){if(e!==void 0){let o=K(i.timestamp);if(!isNaN(o)&&o>=e)break}let a=i.values.find(o=>o.id===s);a&&(t=a)}return t}function Kr(r,s,e){let t=xe(s).find(a=>a.id===e);if(!t)return null;let i=null;for(let a of Wr(r)){if(a.timestamp>=s.timestamp)break;let o=xe(a).find(d=>d.id===e);o&&(i=o.value)}return i==null?null:t.value-i}function Yr(r){let s=new Set,e=new Set;for(let t of r){let i=(t.name||"").trim().toLowerCase();i&&(s.has(i)?e.add(t.id):s.add(i))}return e}function Qr(r){let s=[],e=new Set,t=new Set;for(let i of r){let a=(i.name||"").trim();if(!(!a||e.has(i.id)||t.has(a.toLowerCase()))&&(e.add(i.id),t.add(a.toLowerCase()),s.push({id:i.id,name:a,unit:(i.unLine truncated
|
||||
<div class="field">
|
||||
${this.label?l`<span class="label">${this.label}${this.required?l`<span class="req">*</span>`:p}</span>`:p}
|
||||
<div class="row">
|
||||
${this._typing?l`<input class="typed" type="text" inputmode="numeric" autocomplete="off"
|
||||
.value=${this._typed}
|
||||
placeholder=${H("1978-03-15",this.lang)}
|
||||
placeholder=${j("1978-03-15",this.lang)}
|
||||
aria-invalid=${this._typedInvalid?"true":"false"}
|
||||
@input=${e=>{this._typed=e.target.value,this._typedInvalid=!1}}
|
||||
@keydown=${this._onTypedKey}
|
||||
@blur=${this._commitTyped} />`:l`<ha-selector
|
||||
.hass=${this.hass}
|
||||
.selector=${this._selector()}
|
||||
.value=${Co(this.kind,this.value)}
|
||||
.value=${Lo(this.kind,this.value)}
|
||||
.required=${this.required}
|
||||
.disabled=${this.disabled}
|
||||
@value-changed=${this._onSelectorChange}
|
||||
@@ -1356,7 +1356,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
}
|
||||
input.typed[aria-invalid="true"] { border-color: var(--error-color, #f44336); }
|
||||
input.typed:focus { outline: 2px solid var(--primary-color); outline-offset: -1px; }
|
||||
`,c([f({attribute:!1})],M.prototype,"hass",2),c([f()],M.prototype,"kind",2),c([f()],M.prototype,"label",2),c([f()],M.prototype,"value",2),c([f()],M.prototype,"helper",2),c([f({type:Boolean})],M.prototype,"clearable",2),c([f({type:Boolean})],M.prototype,"disabled",2),c([f({type:Boolean})],M.prototype,"required",2),c([f()],M.prototype,"lang",2),c([u()],M.prototype,"_typing",2),c([u()],M.prototype,"_typed",2),c([u()],M.prototype,"_typedInvalid",2);customElements.get("ms-date-field")||customElements.define("ms-date-field",M)});var $e,hi=y(()=>{"use strict";R();j();C();$e=class extends E{constructor(){super(...arguments);this.value="";this.lang="en";this._credit=!1;this._amount=""}willUpdate(e){if(!e.has("value"))return;let t=(this.value??"").trim();if(t===this._signed())return;let i=parseFloat(t);if(t===""||Number.isNaN(i)){this._amount="",this._credit=!1;return}this._credit=i<0,this._amount=String(Math.abs(i))}_signed(){let e=this._amount.trim();return e===""?"":this._credit?`-${e}`:e}_emit(){this.value=this._signed(),this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:this.value},bubbles:!0,composed:!0}))}_setCredit(e){this._credit!==e&&(this._credit=e,this._emit())}_onInput(e){let t=e.target.value;t.startsWith("-")&&(this._credit=!0),this._amount=t.replace(/^-+/,""),this._emit()}render(){let e=this.lang;return l`
|
||||
`,c([f({attribute:!1})],M.prototype,"hass",2),c([f()],M.prototype,"kind",2),c([f()],M.prototype,"label",2),c([f()],M.prototype,"value",2),c([f()],M.prototype,"helper",2),c([f({type:Boolean})],M.prototype,"clearable",2),c([f({type:Boolean})],M.prototype,"disabled",2),c([f({type:Boolean})],M.prototype,"required",2),c([f()],M.prototype,"lang",2),c([u()],M.prototype,"_typing",2),c([u()],M.prototype,"_typed",2),c([u()],M.prototype,"_typedInvalid",2);customElements.get("ms-date-field")||customElements.define("ms-date-field",M)});var $e,_i=y(()=>{"use strict";R();H();C();$e=class extends E{constructor(){super(...arguments);this.value="";this.lang="en";this._credit=!1;this._amount=""}willUpdate(e){if(!e.has("value"))return;let t=(this.value??"").trim();if(t===this._signed())return;let i=parseFloat(t);if(t===""||Number.isNaN(i)){this._amount="",this._credit=!1;return}this._credit=i<0,this._amount=String(Math.abs(i))}_signed(){let e=this._amount.trim();return e===""?"":this._credit?`-${e}`:e}_emit(){this.value=this._signed(),this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:this.value},bubbles:!0,composed:!0}))}_setCredit(e){this._credit!==e&&(this._credit=e,this._emit())}_onInput(e){let t=e.target.value;t.startsWith("-")&&(this._credit=!0),this._amount=t.replace(/^-+/,""),this._emit()}render(){let e=this.lang;return l`
|
||||
<div class="wrap">
|
||||
<div class="sign" role="group">
|
||||
<button type="button" class="kind cost ${this._credit?"":"on"}" aria-pressed=${String(!this._credit)}
|
||||
@@ -1391,8 +1391,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.kind.credit.on { background: var(--success-color, #43a047); }
|
||||
.amount { flex: 1; min-width: 0; }
|
||||
.hint { margin-top: 4px; font-size: 12px; color: var(--secondary-text-color); }
|
||||
`],c([f()],$e.prototype,"value",2),c([f()],$e.prototype,"lang",2),c([u()],$e.prototype,"_credit",2),c([u()],$e.prototype,"_amount",2);customElements.get("ms-cost-input")||customElements.define("ms-cost-input",$e)});function _i(){if(window.externalApp!==void 0)return!0;let s=typeof navigator<"u"&&navigator.userAgent||"";return/Home Assistant\//.test(s)&&/Android/.test(s)}var hs=y(()=>{"use strict"});function Ot(r){try{return localStorage.getItem(r)}catch{return null}}function jt(r,s){try{localStorage.setItem(r,s)}catch{}}var Ee,_s=y(()=>{"use strict";Ee={objectSections:"msp-object-sections",printOptions:"msp-print-options",batteryRosterOpen:"msp-bf-roster-open",docSort:"msp-doc-sort",cameraLabel:"msp-camera-label",overviewTab:"msp-overview-tab",collapsedSections:"msp-collapsed-sections",chartRange:"msp-chart-range",chartHideOutliers:"msp-chart-hide-outliers",taskSort:"maintenance_supporter_sort",objectSort:"maintenance_supporter_object_sort",groupBy:"maintenance_supporter_groupby",objectView:"maintenance_supporter_object_view",objectsCache:"msp-objects-cache",gettingStartedDismissed:"msp-gs-dismissed",batteryRosterSort:"ms_bf_roster_sort",areaSort:"msp-area-sort"}});function xn(){if(!_i())return!1;let r=typeof navigator<"u"?navigator.mediaDevices:void 0;return!!r&&typeof r.getUserMedia=="function"}function vn(r){let s=/camera2?\s*(\d+)/i.exec(r);return s?Number(s[1]):Number.MAX_SAFE_INTEGER}function bn(r,s){if(s){let t=r.find(i=>i.label===s);if(t)return t}return r.filter(t=>t.label&&wn.test(t.label)).sort((t,i)=>vn(t.label)-vn(i.label))[0]??null}var Io,Po,Lo,No,gn,wn,fn,yn,Oo,Y,ms=y(()=>{"use strict";R();j();C();hs();_s();Io=1920,Po=.88,Lo={retryMs:500,windowMs:5e3},No=new Set(["NotReadableError","AbortError","TrackStartError"]),gn=80,wn=/back|rear|environment|rück|hinten/i,fn=/front|user|selfie|vorne/i;yn=r=>r?.name||"Error",Oo=r=>r?.message||"",Y=class extends E{constructor(){super(...arguments);this.lang="en";this._open=!1;this._busy=!1;this._switching=!1;this._devices=[];this._deviceIndex=-1;this._switchFailed=!1;this._copied=!1;this.timing={...Lo};this._facing="environment";this._stream=null;this._opening=!1;this._gen=0;this._refused=new Set;this._log=[];this._t0=0;this._lastError=""}async open(){if(this._open||this._opening)return;this._opening=!0;let e=++this._gen;try{await this._acquireAndShow(e)}finally{this._opening=!1}}_live(e){return e===this._gen&&this.isConnected}async _acquireAndShow(e){let t=typeof navigator<"u"?navigator.mediaDevices:void 0;if(!t||typeof t.getUserMedia!="function"){this._unavailable("no_media_devices");return}this._log=[],this._t0=Date.now(),this._lastError="",this._refused.clear(),this._switchFailed=!1,this._copied=!1,this._note(navigator.userAgent);let i=Ot(Ee.cameraLabel),a=await this._enumerate(t);if(!this._live(e))return;let o=bn(a,i),d="refused";if(o&&(d=await this._openPatiently(t,e,this._byDevice(o))),d==="cancelled")return;if(d!=="ok"){if(d=await this._openPatiently(t,e,{video:{facingMode:{ideal:"environment"}},what:"facing environment"}),d==="cancelled")return;if(d!=="ok"){this._unavailable(this._lastError||"camera_unavailable");return}if(a=await this._enumerate(t),!this._live(e)){this._stopStream();return}}if(this._devices=a,this._deviceIndex=this._indexOfCurrent(),this._facing=this._currentFacing(),this._open=!0,await this.updateComplete,!this._live(e)){this._stopStream(),this._open=!1;return}await this._attach();let h=o?null:bn(a,i),_=this._deviceIndex>=0?a[this._deviceIndex]:void 0;if(!h||h.deviceId===_?.deviceId)return;let m=await this._replace(t,e,[this._byDevice(h)],this._restoreCandidates(_));if(m!=="cancelled"){if(m==="lost"){this._unavailable("camera_lost");return}this._settle(m)}}async _openPatiently(e,t,i){let a=Date.now();for(;;){let o;try{o=await e.getUserMedia({video:i.video,audio:!1})}catch(d){let h=yn(d),_=Oo(d);if(this._lastError=h,this._note(`${i.what} \u2192 ${h}${_&&_!==h?`: ${_}`:""}`),!this._live(t))return"cancelled";if(!No.has(h))return"refused";if(Date.now()-a+this.timing.retryMs>this.timing.windowMs)return"busy";if(await new Promise(m=>setTimeout(m,this.timing.retryMs)),!this._live(t))return"cancelled";continue}if(!this._live(t)){for(let d of o.getTracks())d.stop();return"cancelled"}return this._stream=o,this._note(`${i.what} \u2192 ok (${this._describeTrack()})`),"ok"}}async _replace(e,t,i,a){this._switching=!0,this._stopStream();try{for(let o of i){let d=await this._openPatiently(e,t,o);if(d==="ok")return o;if(d==="cancelled")return d;if(d==="busy"){o.deviceId&&this._refused.add(o.deviceId);break}}for(let o of a){let d=await this._openPatiently(e,t,o);if(d==="ok")return"restored";if(d==="cancelled")return d}return"lost"}finally{this._switching=!1}}_settle(e){if(e==="restored"){this._switchFailed=!0;let i=this._indexOfCurrent();return i>=0&&(this._deviceIndex=i),this._attach(),!1}this._switchFailed=!1;let t=e.deviceId?this._devices.findIndex(i=>i.deviceId===e.deviceId):-1;return this._deviceIndex=t>=0?t:this._indexOfCurrent(),this._
Line truncated
|
||||
`),t=!1;try{navigator.clipboard?.writeText&&(await navigator.clipboard.writeText(e),t=!0)}catch{}if(!t){let i=this.shadowRoot?.querySelector(".details textarea");if(i){i.focus(),i.select();try{t=document.execCommand("copy")}catch{t=!1}}}this._copied=t}async _shoot(){let e=this._video;if(!(!e||this._busy||this._switching)){this._busy=!0;try{let t=e.videoWidth||640,i=e.videoHeight||480,a=Math.min(1,Io/Math.max(t,i)),o=document.createElement("canvas");o.width=Math.round(t*a),o.height=Math.round(i*a);let d=o.getContext("2d");if(!d)throw new Error("no_canvas");d.drawImage(e,0,0,o.width,o.height);let h=await new Promise(b=>o.toBlob(b,"image/jpeg",Po));if(!h)throw new Error("no_blob");let _=new Date().toISOString().replace(/[-:]/g,"").slice(0,15),m=new File([h],`photo-${_}.jpg`,{type:"image/jpeg"});this.close(),this.dispatchEvent(new CustomEvent("photo-captured",{detail:{file:m},bubbles:!0,composed:!0}))}catch(t){this._unavailable(t instanceof Error?t.message:String(t))}finally{this._busy=!1}}}render(){if(!this._open)return p;let e=this.lang,t=this._busy||this._switching;return l`
|
||||
`],c([f()],$e.prototype,"value",2),c([f()],$e.prototype,"lang",2),c([u()],$e.prototype,"_credit",2),c([u()],$e.prototype,"_amount",2);customElements.get("ms-cost-input")||customElements.define("ms-cost-input",$e)});function mi(){if(window.externalApp!==void 0)return!0;let s=typeof navigator<"u"&&navigator.userAgent||"";return/Home Assistant\//.test(s)&&/Android/.test(s)}var _s=y(()=>{"use strict"});function Ot(r){try{return localStorage.getItem(r)}catch{return null}}function Ht(r,s){try{localStorage.setItem(r,s)}catch{}}var Ee,ms=y(()=>{"use strict";Ee={objectSections:"msp-object-sections",printOptions:"msp-print-options",batteryRosterOpen:"msp-bf-roster-open",docSort:"msp-doc-sort",cameraLabel:"msp-camera-label",overviewTab:"msp-overview-tab",collapsedSections:"msp-collapsed-sections",chartRange:"msp-chart-range",chartHideOutliers:"msp-chart-hide-outliers",taskSort:"maintenance_supporter_sort",objectSort:"maintenance_supporter_object_sort",groupBy:"maintenance_supporter_groupby",objectView:"maintenance_supporter_object_view",objectsCache:"msp-objects-cache",gettingStartedDismissed:"msp-gs-dismissed",batteryRosterSort:"ms_bf_roster_sort",areaSort:"msp-area-sort"}});function wn(){if(!mi())return!1;let r=typeof navigator<"u"?navigator.mediaDevices:void 0;return!!r&&typeof r.getUserMedia=="function"}function bn(r){let s=/camera2?\s*(\d+)/i.exec(r);return s?Number(s[1]):Number.MAX_SAFE_INTEGER}function yn(r,s){if(s){let t=r.find(i=>i.label===s);if(t)return t}return r.filter(t=>t.label&&kn.test(t.label)).sort((t,i)=>bn(t.label)-bn(i.label))[0]??null}var Oo,Ho,jo,qo,fn,kn,vn,xn,Do,Y,gs=y(()=>{"use strict";R();H();C();_s();ms();Oo=1920,Ho=.88,jo={retryMs:500,windowMs:5e3},qo=new Set(["NotReadableError","AbortError","TrackStartError"]),fn=80,kn=/back|rear|environment|rück|hinten/i,vn=/front|user|selfie|vorne/i;xn=r=>r?.name||"Error",Do=r=>r?.message||"",Y=class extends E{constructor(){super(...arguments);this.lang="en";this._open=!1;this._busy=!1;this._switching=!1;this._devices=[];this._deviceIndex=-1;this._switchFailed=!1;this._copied=!1;this.timing={...jo};this._facing="environment";this._stream=null;this._opening=!1;this._gen=0;this._refused=new Set;this._log=[];this._t0=0;this._lastError=""}async open(){if(this._open||this._opening)return;this._opening=!0;let e=++this._gen;try{await this._acquireAndShow(e)}finally{this._opening=!1}}_live(e){return e===this._gen&&this.isConnected}async _acquireAndShow(e){let t=typeof navigator<"u"?navigator.mediaDevices:void 0;if(!t||typeof t.getUserMedia!="function"){this._unavailable("no_media_devices");return}this._log=[],this._t0=Date.now(),this._lastError="",this._refused.clear(),this._switchFailed=!1,this._copied=!1,this._note(navigator.userAgent);let i=Ot(Ee.cameraLabel),a=await this._enumerate(t);if(!this._live(e))return;let o=yn(a,i),d="refused";if(o&&(d=await this._openPatiently(t,e,this._byDevice(o))),d==="cancelled")return;if(d!=="ok"){if(d=await this._openPatiently(t,e,{video:{facingMode:{ideal:"environment"}},what:"facing environment"}),d==="cancelled")return;if(d!=="ok"){this._unavailable(this._lastError||"camera_unavailable");return}if(a=await this._enumerate(t),!this._live(e)){this._stopStream();return}}if(this._devices=a,this._deviceIndex=this._indexOfCurrent(),this._facing=this._currentFacing(),this._open=!0,await this.updateComplete,!this._live(e)){this._stopStream(),this._open=!1;return}await this._attach();let h=o?null:yn(a,i),_=this._deviceIndex>=0?a[this._deviceIndex]:void 0;if(!h||h.deviceId===_?.deviceId)return;let m=await this._replace(t,e,[this._byDevice(h)],this._restoreCandidates(_));if(m!=="cancelled"){if(m==="lost"){this._unavailable("camera_lost");return}this._settle(m)}}async _openPatiently(e,t,i){let a=Date.now();for(;;){let o;try{o=await e.getUserMedia({video:i.video,audio:!1})}catch(d){let h=xn(d),_=Do(d);if(this._lastError=h,this._note(`${i.what} \u2192 ${h}${_&&_!==h?`: ${_}`:""}`),!this._live(t))return"cancelled";if(!qo.has(h))return"refused";if(Date.now()-a+this.timing.retryMs>this.timing.windowMs)return"busy";if(await new Promise(m=>setTimeout(m,this.timing.retryMs)),!this._live(t))return"cancelled";continue}if(!this._live(t)){for(let d of o.getTracks())d.stop();return"cancelled"}return this._stream=o,this._note(`${i.what} \u2192 ok (${this._describeTrack()})`),"ok"}}async _replace(e,t,i,a){this._switching=!0,this._stopStream();try{for(let o of i){let d=await this._openPatiently(e,t,o);if(d==="ok")return o;if(d==="cancelled")return d;if(d==="busy"){o.deviceId&&this._refused.add(o.deviceId);break}}for(let o of a){let d=await this._openPatiently(e,t,o);if(d==="ok")return"restored";if(d==="cancelled")return d}return"lost"}finally{this._switching=!1}}_settle(e){if(e==="restored"){this._switchFailed=!0;let i=this._indexOfCurrent();return i>=0&&(this._deviceIndex=i),this._attach(),!1}this._switchFailed=!1;let t=e.deviceId?this._devices.findIndex(i=>i.deviceId===e.deviceId):-1;return this._deviceIndex=t>=0?t:this._indexOfCurrent(),this._
Line truncated
|
||||
`),t=!1;try{navigator.clipboard?.writeText&&(await navigator.clipboard.writeText(e),t=!0)}catch{}if(!t){let i=this.shadowRoot?.querySelector(".details textarea");if(i){i.focus(),i.select();try{t=document.execCommand("copy")}catch{t=!1}}}this._copied=t}async _shoot(){let e=this._video;if(!(!e||this._busy||this._switching)){this._busy=!0;try{let t=e.videoWidth||640,i=e.videoHeight||480,a=Math.min(1,Oo/Math.max(t,i)),o=document.createElement("canvas");o.width=Math.round(t*a),o.height=Math.round(i*a);let d=o.getContext("2d");if(!d)throw new Error("no_canvas");d.drawImage(e,0,0,o.width,o.height);let h=await new Promise(b=>o.toBlob(b,"image/jpeg",Ho));if(!h)throw new Error("no_blob");let _=new Date().toISOString().replace(/[-:]/g,"").slice(0,15),m=new File([h],`photo-${_}.jpg`,{type:"image/jpeg"});this.close(),this.dispatchEvent(new CustomEvent("photo-captured",{detail:{file:m},bubbles:!0,composed:!0}))}catch(t){this._unavailable(t instanceof Error?t.message:String(t))}finally{this._busy=!1}}}render(){if(!this._open)return p;let e=this.lang,t=this._busy||this._switching;return l`
|
||||
<div class="overlay" role="dialog" aria-modal="true" aria-label=${n("doc_camera",e)}>
|
||||
<video autoplay playsinline muted></video>
|
||||
${this._switching?l`<div class="switching" role="status">${n("camera_switching",e)}</div>`:p}
|
||||
@@ -1457,7 +1457,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.shoot { background: var(--primary-color, #03a9f4); color: #fff; border: none; font-weight: 600; }
|
||||
.shoot[disabled] { opacity: 0.6; cursor: default; }
|
||||
.shoot ha-icon { --mdc-icon-size: 22px; }
|
||||
`,c([f({type:String})],Y.prototype,"lang",2),c([u()],Y.prototype,"_open",2),c([u()],Y.prototype,"_busy",2),c([u()],Y.prototype,"_switching",2),c([u()],Y.prototype,"_devices",2),c([u()],Y.prototype,"_deviceIndex",2),c([u()],Y.prototype,"_switchFailed",2),c([u()],Y.prototype,"_copied",2);customElements.get("ms-camera-capture")||customElements.define("ms-camera-capture",Y)});var mi,Q,Ht=y(()=>{"use strict";R();j();C();hs();ms();ms();mi=$`
|
||||
`,c([f({type:String})],Y.prototype,"lang",2),c([u()],Y.prototype,"_open",2),c([u()],Y.prototype,"_busy",2),c([u()],Y.prototype,"_switching",2),c([u()],Y.prototype,"_devices",2),c([u()],Y.prototype,"_deviceIndex",2),c([u()],Y.prototype,"_switchFailed",2),c([u()],Y.prototype,"_copied",2);customElements.get("ms-camera-capture")||customElements.define("ms-camera-capture",Y)});var gi,Q,jt=y(()=>{"use strict";R();H();C();_s();gs();gs();gi=$`
|
||||
ms-photo-picker { display: contents; }
|
||||
.photo-pickers {
|
||||
display: flex;
|
||||
@@ -1486,7 +1486,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
color: var(--secondary-text-color);
|
||||
margin-top: 4px;
|
||||
}
|
||||
`,Q=class extends E{constructor(){super(...arguments);this.lang="en";this.disabled=!1;this.busy=!1;this.accept="image/*";this.showCamera=!0;this.showGallery=!0;this.compact=!1;this.remaining=1/0;this._singlePick=_i();this._inAppCamera=xn()}createRenderRoot(){return this}get _locked(){return this.disabled||this.busy}_emit(e){e.length!==0&&this.dispatchEvent(new CustomEvent("files-picked",{detail:{files:e},bubbles:!0,composed:!0}))}_onInput(e){let t=e.target,i=Array.from(t.files??[]);t.value="",this._emit(i)}_onCameraClick(e){!this._inAppCamera||this._locked||(e.preventDefault(),this.querySelector("ms-camera-capture")?.open())}_onCameraUnavailable(){this._inAppCamera=!1,this.querySelector(".photo-pick-camera input")?.click()}_onKeydown(e){e.key!=="Enter"&&e.key!==" "||(e.preventDefault(),!this._locked&&e.currentTarget.click())}render(){if(this.remaining<=0||!this.showCamera&&!this.showGallery)return p;let e=this.lang,t=this._locked?"disabled":"",i=this.busy?n("uploading",e):n("doc_camera",e),a=n(this._singlePick?"choose_photo":"choose_photos",e);return l`
|
||||
`,Q=class extends E{constructor(){super(...arguments);this.lang="en";this.disabled=!1;this.busy=!1;this.accept="image/*";this.showCamera=!0;this.showGallery=!0;this.compact=!1;this.remaining=1/0;this._singlePick=mi();this._inAppCamera=wn()}createRenderRoot(){return this}get _locked(){return this.disabled||this.busy}_emit(e){e.length!==0&&this.dispatchEvent(new CustomEvent("files-picked",{detail:{files:e},bubbles:!0,composed:!0}))}_onInput(e){let t=e.target,i=Array.from(t.files??[]);t.value="",this._emit(i)}_onCameraClick(e){!this._inAppCamera||this._locked||(e.preventDefault(),this.querySelector("ms-camera-capture")?.open())}_onCameraUnavailable(){this._inAppCamera=!1,this.querySelector(".photo-pick-camera input")?.click()}_onKeydown(e){e.key!=="Enter"&&e.key!==" "||(e.preventDefault(),!this._locked&&e.currentTarget.click())}render(){if(this.remaining<=0||!this.showCamera&&!this.showGallery)return p;let e=this.lang,t=this._locked?"disabled":"",i=this.busy?n("uploading",e):n("doc_camera",e),a=n(this._singlePick?"choose_photo":"choose_photos",e);return l`
|
||||
<div class="photo-pickers">
|
||||
${this.showCamera?l`<label class="photo-pick photo-pick-camera ${t}" role="button" tabindex="0"
|
||||
aria-label=${i} title=${this.compact?i:p}
|
||||
@@ -1509,7 +1509,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
${this.showCamera&&this._inAppCamera?l`<ms-camera-capture .lang=${e}
|
||||
@photo-captured=${o=>this._emit([o.detail.file])}
|
||||
@capture-unavailable=${this._onCameraUnavailable}></ms-camera-capture>`:p}
|
||||
`}};c([f({type:String})],Q.prototype,"lang",2),c([f({type:Boolean})],Q.prototype,"disabled",2),c([f({type:Boolean})],Q.prototype,"busy",2),c([f({type:String})],Q.prototype,"accept",2),c([f({type:Boolean})],Q.prototype,"showCamera",2),c([f({type:Boolean})],Q.prototype,"showGallery",2),c([f({type:Boolean})],Q.prototype,"compact",2),c([f({type:Number})],Q.prototype,"remaining",2),c([u()],Q.prototype,"_inAppCamera",2);customElements.get("ms-photo-picker")||customElements.define("ms-photo-picker",Q)});var nt,S,gs=y(()=>{"use strict";R();j();Oe();C();ke();ee();Lt();ai();ls();ds();us();ui();rt();hi();Ht();Ht();nt=we,S=class extends E{constructor(){super(...arguments);this.entryId="";this.taskId="";this.taskName="";this.lang="en";this.checklist=[];this.adaptiveEnabled=!1;this.taskType="";this.readingUnit="";this.readings=[];this.readingHistory=[];this.restockDefault=null;this.restockUnitCost=null;this.restockPackage="";this.currencySymbol="";this.partsCostMode="purchase";this.parts=[];this.consumesParts=[];this.consumesInfo=[];this.requiredFields=[];this.phaseLabel="";this.requireTagScan=!1;this.viaTagScan=!1;this._open=!1;this._notes="";this._cost="";this._duration="";this._loading=!1;this._error="";this._checklistState={};this._feedback="needed";this._photos=new tt(this,{entryId:()=>this.entryId,hass:()=>this.hass});this._readingValue="";this._readingValues={};this._restockQty="";this._completedAt="";this._usedParts={};this._usedQtyText={};this.checklistPrefill={}}open(e={}){this._open||(this._open=!0,this.viaTagScan=!!e.viaTagScan,this._notes="",this._cost="",this._duration="",this._error="",this._checklistState=Object.fromEntries(this.checklist.map((t,i)=>[String(i),!!this.checklistPrefill[t]]).filter(([,t])=>t)),this._feedback="needed",this._photos.reset(),this._readingValue="",this._readingValues={},this._restockQty=this.restockDefault!==null?String(this.restockDefault):"",this._completedAt="",this._usedParts=Object.fromEntries(this.consumesParts.map(t=>[ne(t),{...t}])),this._usedQtyText={})}_rangeError(e,t){let i=this.lang;return n("settings_value_out_of_range",i).replace("{min}",I(e,i,{maximumFractionDigits:2})).replace("{max}",I(t,i,{maximumFractionDigits:2}))}_toggleCheck(e){let t=String(e);this._checklistState={...this._checklistState,[t]:!this._checklistState[t]}}_setFeedback(e){this._feedback=e}async _complete(){if(this._loading||this._photos.uploading)return;this._error="",this._photos.clearError();let e={type:"maintenance_supporter/task/complete",entry_id:this.entryId,task_id:this.taskId},t=this._notes.trim();if(t&&(e.notes=t),this._cost){let d=parseFloat(this._cost);isNaN(d)||(e.cost=d)}let i=it(this._duration);if(i!==null&&(e.duration=i),this.checklist.length>0&&(e.checklist_state=this._checklistState),this.adaptiveEnabled&&(e.feedback=this._feedback),this._photos.photos.length>0&&(e.photo_doc_ids=this._photos.ids),this.viaTagScan&&(e.via_tag_scan=!0),this._completedAt){if(G(this._completedAt)>Date.now()){this._error=n("completed_at_future_error",this.lang);return}e.completed_at=this._completedAt.length===16?`${this._completedAt}:00`:this._completedAt}if(this.readings.length>0){let d={};for(let h of this.readings){let _=(this._readingValues[h.id]??"").trim();if(_==="")continue;let m=parseFloat(_.replace(",","."));isNaN(m)||(d[h.id]=m)}Object.keys(d).length>0&&(e.reading_values=d)}else if(this._readingValue!==""){let d=parseFloat(this._readingValue);isNaN(d)||(e.reading_value=d)}if(this.restockDefault!==null&&this._restockQty.trim()!==""){let d=parseFloat(this._restockQty.replace(",","."));if(!Number.isFinite(d)||d<Je[0]||d>Je[1]){this._error=this._rangeError(...Je);return}e.restock_quantity=d}if(this.parts.length>0){let d=[];for(let[h,_]of Object.entries(this._usedParts)){let m=this._usedQtyText[h],b=m===void 0?_.quantity:parseFloat(m.replace(",","."));if(!Number.isFinite(b)||b<nt[0]||b>nt[1]){this._error=this._rangeError(...nt);return}d.push({..._,quantity:b})}e.used_parts=d.map(h=>h.entry_id?{part_id:h.part_id,quantity:h.quantity,entry_id:h.entry_id}:{part_id:h.part_id,quantity:h.quantity})}let a=await q(this,e,{busy:d=>{this._loading=d},lang:this.lang,fallbackKey:"save_error",onError:d=>{this._error=d}});if(a===void 0)return;this._photos.markAttached(),this._open=!1;let o={entryId:this.entryId,taskId:this.taskId,taskName:this.taskName,undo:a?.undo===!0};this.dispatchEvent(new CustomEvent("task-completed",{detail:o}))}_renderReadingField(e,t){let i=this._completedAt?G(this._completedAt):NaN,a=Vr(this.readingHistory,e.id,isNaN(i)?void 0:i),o=e.unit||this.readingUnit,d=(this._readingValues[e.id]??"").trim(),h=d===""?NaN:parseFloat(d.replace(",",".")),_=a!==void 0&&!isNaN(h)&&h<a.value,m=a!==void 0?I(a.value,t,{maximumFractionDigits:3}):"";return l`
|
||||
`}};c([f({type:String})],Q.prototype,"lang",2),c([f({type:Boolean})],Q.prototype,"disabled",2),c([f({type:Boolean})],Q.prototype,"busy",2),c([f({type:String})],Q.prototype,"accept",2),c([f({type:Boolean})],Q.prototype,"showCamera",2),c([f({type:Boolean})],Q.prototype,"showGallery",2),c([f({type:Boolean})],Q.prototype,"compact",2),c([f({type:Number})],Q.prototype,"remaining",2),c([u()],Q.prototype,"_inAppCamera",2);customElements.get("ms-photo-picker")||customElements.define("ms-photo-picker",Q)});var nt,S,fs=y(()=>{"use strict";R();H();Oe();C();ke();ee();Lt();oi();cs();ps();hs();hi();rt();_i();jt();jt();nt=we,S=class extends E{constructor(){super(...arguments);this.entryId="";this.taskId="";this.taskName="";this.lang="en";this.checklist=[];this.adaptiveEnabled=!1;this.taskType="";this.readingUnit="";this.readings=[];this.readingHistory=[];this.restockDefault=null;this.restockUnitCost=null;this.restockPackage="";this.currencySymbol="";this.partsCostMode="purchase";this.parts=[];this.consumesParts=[];this.consumesInfo=[];this.requiredFields=[];this.phaseLabel="";this.requireTagScan=!1;this.viaTagScan=!1;this._open=!1;this._notes="";this._cost="";this._duration="";this._loading=!1;this._error="";this._checklistState={};this._feedback="needed";this._photos=new tt(this,{entryId:()=>this.entryId,hass:()=>this.hass});this._readingValue="";this._readingValues={};this._restockQty="";this._completedAt="";this._usedParts={};this._usedQtyText={};this.checklistPrefill={}}open(e={}){this._open||(this._open=!0,this.viaTagScan=!!e.viaTagScan,this._notes="",this._cost="",this._duration="",this._error="",this._checklistState=Object.fromEntries(this.checklist.map((t,i)=>[String(i),!!this.checklistPrefill[t]]).filter(([,t])=>t)),this._feedback="needed",this._photos.reset(),this._readingValue="",this._readingValues={},this._restockQty=this.restockDefault!==null?String(this.restockDefault):"",this._completedAt="",this._usedParts=Object.fromEntries(this.consumesParts.map(t=>[ne(t),{...t}])),this._usedQtyText={})}_rangeError(e,t){let i=this.lang;return n("settings_value_out_of_range",i).replace("{min}",I(e,i,{maximumFractionDigits:2})).replace("{max}",I(t,i,{maximumFractionDigits:2}))}_toggleCheck(e){let t=String(e);this._checklistState={...this._checklistState,[t]:!this._checklistState[t]}}_setFeedback(e){this._feedback=e}async _complete(){if(this._loading||this._photos.uploading)return;this._error="",this._photos.clearError();let e={type:"maintenance_supporter/task/complete",entry_id:this.entryId,task_id:this.taskId},t=this._notes.trim();if(t&&(e.notes=t),this._cost){let d=parseFloat(this._cost);isNaN(d)||(e.cost=d)}let i=it(this._duration);if(i!==null&&(e.duration=i),this.checklist.length>0&&(e.checklist_state=this._checklistState),this.adaptiveEnabled&&(e.feedback=this._feedback),this._photos.photos.length>0&&(e.photo_doc_ids=this._photos.ids),this.viaTagScan&&(e.via_tag_scan=!0),this._completedAt){if(K(this._completedAt)>Date.now()){this._error=n("completed_at_future_error",this.lang);return}e.completed_at=this._completedAt.length===16?`${this._completedAt}:00`:this._completedAt}if(this.readings.length>0){let d={};for(let h of this.readings){let _=(this._readingValues[h.id]??"").trim();if(_==="")continue;let m=parseFloat(_.replace(",","."));isNaN(m)||(d[h.id]=m)}Object.keys(d).length>0&&(e.reading_values=d)}else if(this._readingValue!==""){let d=parseFloat(this._readingValue);isNaN(d)||(e.reading_value=d)}if(this.restockDefault!==null&&this._restockQty.trim()!==""){let d=parseFloat(this._restockQty.replace(",","."));if(!Number.isFinite(d)||d<Je[0]||d>Je[1]){this._error=this._rangeError(...Je);return}e.restock_quantity=d}if(this.parts.length>0){let d=[];for(let[h,_]of Object.entries(this._usedParts)){let m=this._usedQtyText[h],b=m===void 0?_.quantity:parseFloat(m.replace(",","."));if(!Number.isFinite(b)||b<nt[0]||b>nt[1]){this._error=this._rangeError(...nt);return}d.push({..._,quantity:b})}e.used_parts=d.map(h=>h.entry_id?{part_id:h.part_id,quantity:h.quantity,entry_id:h.entry_id}:{part_id:h.part_id,quantity:h.quantity})}let a=await q(this,e,{busy:d=>{this._loading=d},lang:this.lang,fallbackKey:"save_error",onError:d=>{this._error=d}});if(a===void 0)return;this._photos.markAttached(),this._open=!1;let o={entryId:this.entryId,taskId:this.taskId,taskName:this.taskName,undo:a?.undo===!0};this.dispatchEvent(new CustomEvent("task-completed",{detail:o}))}_renderReadingField(e,t){let i=this._completedAt?K(this._completedAt):NaN,a=Gr(this.readingHistory,e.id,isNaN(i)?void 0:i),o=e.unit||this.readingUnit,d=(this._readingValues[e.id]??"").trim(),h=d===""?NaN:parseFloat(d.replace(",",".")),_=a!==void 0&&!isNaN(h)&&h<a.value,m=a!==void 0?I(a.value,t,{maximumFractionDigits:3}):"";return l`
|
||||
<label class="field reading-field">
|
||||
<span class="field-label">${e.name}${o?` (${o})`:""}</span>
|
||||
<input type="text" inputmode="decimal" class="field-input"
|
||||
@@ -1517,11 +1517,11 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._readingValues[e.id]??""}
|
||||
@input=${b=>{this._readingValues={...this._readingValues,[e.id]:b.target.value}}} />
|
||||
${_?l`<span class="reading-warn">${n("reading_below_last",t).replace("{value}",m)}</span>`:p}
|
||||
</label>`}get _missingRequired(){let e={notes:this._notes.trim()!=="",cost:this._cost.trim()!=="",duration:this._duration.trim()!=="",photo:this._photos.photos.length>0,user:!!this.hass?.user};return this.requiredFields.filter(t=>!e[t])}_demands(e){return this.requiredFields.includes(e)}_req(e){return this._demands(e)?l`<span class="req-mark" aria-hidden="true">*</span>`:p}_purchaseSuggestion(){if(this.restockDefault===null)return null;let e=parseFloat(this._restockQty);return this.restockUnitCost==null||!Number.isFinite(e)||e<=0?null:Math.round(this.restockUnitCost*e*100)/100}_partsValue(){if(this.restockDefault!==null||!this.parts.length)return null;let e=0,t=!1;for(let i of Object.values(this._usedParts)){let a=this.parts.find(d=>ne({part_id:d.id,entry_id:d.entry_id})===ne(i)),o=nn(a);o!==null&&(e+=o*(i.quantity||1),t=!0)}return t?Math.round(e*100)/100:null}_renderCostSuggestion(e){let t=this._purchaseSuggestion();if(this.restockDefault!==null){let d=this.partsCostMode==="use"?l`<div class="cost-note">${n("cost_purchase_use_hint",e)}</div>`:p;if(this._cost.trim()!==""||t==null||t<=0)return d;let h=re(t,this.currencySymbol,e);return l`<button
|
||||
</label>`}get _missingRequired(){let e={notes:this._notes.trim()!=="",cost:this._cost.trim()!=="",duration:this._duration.trim()!=="",photo:this._photos.photos.length>0,user:!!this.hass?.user};return this.requiredFields.filter(t=>!e[t])}_demands(e){return this.requiredFields.includes(e)}_req(e){return this._demands(e)?l`<span class="req-mark" aria-hidden="true">*</span>`:p}_purchaseSuggestion(){if(this.restockDefault===null)return null;let e=parseFloat(this._restockQty);return this.restockUnitCost==null||!Number.isFinite(e)||e<=0?null:Math.round(this.restockUnitCost*e*100)/100}_partsValue(){if(this.restockDefault!==null||!this.parts.length)return null;let e=0,t=!1;for(let i of Object.values(this._usedParts)){let a=this.parts.find(d=>ne({part_id:d.id,entry_id:d.entry_id})===ne(i)),o=an(a);o!==null&&(e+=o*(i.quantity||1),t=!0)}return t?Math.round(e*100)/100:null}_renderCostSuggestion(e){let t=this._purchaseSuggestion();if(this.restockDefault!==null){let d=this.partsCostMode==="use"?l`<div class="cost-note">${n("cost_purchase_use_hint",e)}</div>`:p;if(this._cost.trim()!==""||t==null||t<=0)return d;let h=re(t,this.currencySymbol,e);return l`<button
|
||||
type="button"
|
||||
class="cost-suggestion"
|
||||
@click=${()=>this._cost=String(Math.round(t*100)/100)}
|
||||
>${n("cost_from_parts",e).replace("{amount}",h)}</button>${d}`}let i=this._partsValue();if(i==null||i<=0)return p;let a=re(i,this.currencySymbol,e),o=this.partsCostMode==="use"?"cost_parts_booked":"cost_parts_info";return l`<div class="cost-note">${n(o,e).replace("{amount}",a)}</div>`}_close(){this._open=!1,this._photos.discardOrphans()}_pickCompletedAt(){this._completedAt=`${Er()}:00`}render(){if(!this._open)return l``;let e=this.lang||this.hass?.language||"en",t=this._error||this._photos.errorText(e);return l`
|
||||
>${n("cost_from_parts",e).replace("{amount}",h)}</button>${d}`}let i=this._partsValue();if(i==null||i<=0)return p;let a=re(i,this.currencySymbol,e),o=this.partsCostMode==="use"?"cost_parts_booked":"cost_parts_info";return l`<div class="cost-note">${n(o,e).replace("{amount}",a)}</div>`}_close(){this._open=!1,this._photos.discardOrphans()}_pickCompletedAt(){this._completedAt=`${Sr()}:00`}render(){if(!this._open)return l``;let e=this.lang||this.hass?.language||"en",t=this._error||this._photos.errorText(e);return l`
|
||||
<ha-dialog open @closed=${this._close}>
|
||||
<div class="dialog-title">${n("complete_title",e)}${this.taskName}</div>
|
||||
${this.phaseLabel?l`<div class="phase-line">${n("phase_current",e)}: ${this.phaseLabel}</div>`:p}
|
||||
@@ -1608,7 +1608,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.hass=${this.hass}
|
||||
.lang=${e}
|
||||
.value=${this._completedAt}
|
||||
.helper=${Xt(e)}
|
||||
.helper=${ei(e)}
|
||||
@value-changed=${i=>this._completedAt=i.detail.value}
|
||||
></ms-date-field>`:l`<button type="button" class="backdate-pick" @click=${this._pickCompletedAt}>
|
||||
<ha-icon icon="mdi:calendar-clock"></ha-icon>${n("completed_at_pick",e)}
|
||||
@@ -1661,7 +1661,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}};S.styles=[Ke,mi,$`
|
||||
`}};S.styles=[Ke,gi,$`
|
||||
.req-mark {
|
||||
color: var(--error-color, #f44336);
|
||||
margin-left: 2px;
|
||||
@@ -1877,7 +1877,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
color: var(--text-primary-color, #fff);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
`],c([f({attribute:!1})],S.prototype,"hass",2),c([f()],S.prototype,"entryId",2),c([f()],S.prototype,"taskId",2),c([f()],S.prototype,"taskName",2),c([f()],S.prototype,"lang",2),c([f({type:Array})],S.prototype,"checklist",2),c([f({type:Boolean})],S.prototype,"adaptiveEnabled",2),c([f()],S.prototype,"taskType",2),c([f()],S.prototype,"readingUnit",2),c([f({attribute:!1})],S.prototype,"readings",2),c([f({attribute:!1})],S.prototype,"readingHistory",2),c([f({attribute:!1})],S.prototype,"restockDefault",2),c([f({attribute:!1})],S.prototype,"restockUnitCost",2),c([f({attribute:!1})],S.prototype,"restockPackage",2),c([f()],S.prototype,"currencySymbol",2),c([f({attribute:!1})],S.prototype,"partsCostMode",2),c([f({attribute:!1})],S.prototype,"parts",2),c([f({attribute:!1})],S.prototype,"consumesParts",2),c([f({type:Array})],S.prototype,"consumesInfo",2),c([f({type:Array})],S.prototype,"requiredFields",2),c([f()],S.prototype,"phaseLabel",2),c([f({type:Boolean})],S.prototype,"requireTagScan",2),c([f({type:Boolean})],S.prototype,"viaTagScan",2),c([u()],S.prototype,"_open",2),c([u()],S.prototype,"_notes",2),c([u()],S.prototype,"_cost",2),c([u()],S.prototype,"_duration",2),c([u()],S.prototype,"_loading",2),c([u()],S.prototype,"_error",2),c([u()],S.prototype,"_checklistState",2),c([u()],S.prototype,"_feedback",2),c([u()],S.prototype,"_readingValue",2),c([u()],S.prototype,"_readingValues",2),c([u()],S.prototype,"_restockQty",2),c([u()],S.prototype,"_completedAt",2),c([u()],S.prototype,"_usedParts",2),c([u()],S.prototype,"_usedQtyText",2),c([f({attribute:!1})],S.prototype,"checklistPrefill",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",S)});function A(r){return r.toFixed(1)}var gi=y(()=>{"use strict";C();ee()});var D,vs=y(()=>{"use strict";R();j();D=class extends E{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1;this.multiline=!1;this.rows=3}_onInput(e){let t=e.target.value;this.value=t,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:t}}))}render(){return l`
|
||||
`],c([f({attribute:!1})],S.prototype,"hass",2),c([f()],S.prototype,"entryId",2),c([f()],S.prototype,"taskId",2),c([f()],S.prototype,"taskName",2),c([f()],S.prototype,"lang",2),c([f({type:Array})],S.prototype,"checklist",2),c([f({type:Boolean})],S.prototype,"adaptiveEnabled",2),c([f()],S.prototype,"taskType",2),c([f()],S.prototype,"readingUnit",2),c([f({attribute:!1})],S.prototype,"readings",2),c([f({attribute:!1})],S.prototype,"readingHistory",2),c([f({attribute:!1})],S.prototype,"restockDefault",2),c([f({attribute:!1})],S.prototype,"restockUnitCost",2),c([f({attribute:!1})],S.prototype,"restockPackage",2),c([f()],S.prototype,"currencySymbol",2),c([f({attribute:!1})],S.prototype,"partsCostMode",2),c([f({attribute:!1})],S.prototype,"parts",2),c([f({attribute:!1})],S.prototype,"consumesParts",2),c([f({type:Array})],S.prototype,"consumesInfo",2),c([f({type:Array})],S.prototype,"requiredFields",2),c([f()],S.prototype,"phaseLabel",2),c([f({type:Boolean})],S.prototype,"requireTagScan",2),c([f({type:Boolean})],S.prototype,"viaTagScan",2),c([u()],S.prototype,"_open",2),c([u()],S.prototype,"_notes",2),c([u()],S.prototype,"_cost",2),c([u()],S.prototype,"_duration",2),c([u()],S.prototype,"_loading",2),c([u()],S.prototype,"_error",2),c([u()],S.prototype,"_checklistState",2),c([u()],S.prototype,"_feedback",2),c([u()],S.prototype,"_readingValue",2),c([u()],S.prototype,"_readingValues",2),c([u()],S.prototype,"_restockQty",2),c([u()],S.prototype,"_completedAt",2),c([u()],S.prototype,"_usedParts",2),c([u()],S.prototype,"_usedQtyText",2),c([f({attribute:!1})],S.prototype,"checklistPrefill",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",S)});function A(r){return r.toFixed(1)}var fi=y(()=>{"use strict";C();ee()});var D,bs=y(()=>{"use strict";R();H();D=class extends E{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1;this.multiline=!1;this.rows=3}_onInput(e){let t=e.target.value;this.value=t,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:t}}))}render(){return l`
|
||||
<label class="field">
|
||||
${this.label?l`<span class="label">${this.label}${this.required?l`<span class="req">*</span>`:p}</span>`:p}
|
||||
${this.multiline?l`
|
||||
@@ -1954,7 +1954,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
color: var(--secondary-text-color);
|
||||
font-style: italic;
|
||||
}
|
||||
`,c([f()],D.prototype,"label",2),c([f()],D.prototype,"value",2),c([f()],D.prototype,"placeholder",2),c([f()],D.prototype,"type",2),c([f({type:Boolean})],D.prototype,"required",2),c([f({type:Boolean})],D.prototype,"disabled",2),c([f()],D.prototype,"step",2),c([f()],D.prototype,"min",2),c([f()],D.prototype,"max",2),c([f()],D.prototype,"pattern",2),c([f()],D.prototype,"helper",2),c([f({type:Boolean})],D.prototype,"multiline",2),c([f({type:Number})],D.prototype,"rows",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",D)});var O,$n=y(()=>{"use strict";R();j();C();ke();vs();rt();O=class extends E{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;this._replacing=!1;this._oldDeviceId="";this._replaceDevice="keep"}get _lang(){return P(this.hass)}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._replacing=!1,this._error="",this._open=!0}openReplace(e,t){this._entryId=e,this._replacing=!0,this._name=t.name||"",this._oldDeviceId=t.ha_device_id||"",this._haDeviceId="",this._replaceDevice="keep",this._error="",this._open=!0}openEdit(e,t){this._entryId=e,this._name=t.name||"",this._manufacturer=t.manufacturer||"",this._model=t.model||"",this._serialNumber=t.serial_number||"",this._areaId=t.area_id||"",this._installationDate=t.installation_date||"",this._warrantyExpiry=t.warranty_expiry||"",this._documentationUrl=t.documentation_url||"",this._notes=t.notes||"",this._haDeviceId=t.ha_device_id||"",this._parentEntryId=t.parent_entry_id||"",this._replacing=!1,this._error="",this._open=!0}async _replace(){if(this._loading||!this._entryId)return;let e={type:"maintenance_supporter/object/replace",entry_id:this._entryId,name:this._name.trim()||null};this._oldDeviceId?(this._replaceDevice==="other"&&(e.ha_device_id=this._haDeviceId),this._replaceDevice==="none"&&(e.ha_device_id=null)):this._haDeviceId&&(e.ha_device_id=this._haDeviceId);let t=await q(this,e,{busy:i=>{this._loading=i},fallbackKey:"save_error",onError:i=>{this._error=i}});t!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-replaced",{detail:{entry_id:t?.entry_id,device_swap:t?.device_swap}})))}_deviceName(e){let i=this.hass?.devices?.[e];return i?.name_by_user||i?.name||e}_deviceSelector(e){return l`<ha-form
|
||||
`,c([f()],D.prototype,"label",2),c([f()],D.prototype,"value",2),c([f()],D.prototype,"placeholder",2),c([f()],D.prototype,"type",2),c([f({type:Boolean})],D.prototype,"required",2),c([f({type:Boolean})],D.prototype,"disabled",2),c([f()],D.prototype,"step",2),c([f()],D.prototype,"min",2),c([f()],D.prototype,"max",2),c([f()],D.prototype,"pattern",2),c([f()],D.prototype,"helper",2),c([f({type:Boolean})],D.prototype,"multiline",2),c([f({type:Number})],D.prototype,"rows",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",D)});var L,En=y(()=>{"use strict";R();H();C();ke();bs();rt();L=class extends E{constructor(){super(...arguments);this.objects=[];this.placesEnabled=!1;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._place="";this._remindOnSite=!1;this._entryId=null;this._replacing=!1;this._oldDeviceId="";this._replaceDevice="keep"}get _lang(){return P(this.hass)}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._place="",this._remindOnSite=!1,this._replacing=!1,this._error="",this._open=!0}openReplace(e,t){this._entryId=e,this._replacing=!0,this._name=t.name||"",this._oldDeviceId=t.ha_device_id||"",this._haDeviceId="",this._replaceDevice="keep",this._error="",this._open=!0}openEdit(e,t){this._entryId=e,this._name=t.name||"",this._manufacturer=t.manufacturer||"",this._model=t.model||"",this._serialNumber=t.serial_number||"",this._areaId=t.area_id||"",this._installationDate=t.installation_date||"",this._warrantyExpiry=t.warranty_expiry||"",this._documentationUrl=t.documentation_url||"",this._notes=t.notes||"",this._haDeviceId=t.ha_device_id||"",this._parentEntryId=t.parent_entry_id||"",this._place=t.place||"",this._remindOnSite=!!t.remind_on_site,this._replacing=!1,this._error="",this._open=!0}async _replace(){if(this._loading||!this._entryId)return;let e={type:"maintenance_supporter/object/replace",entry_id:this._entryId,name:this._name.trim()||null};this._oldDeviceId?(this._replaceDevice==="other"&&(e.ha_device_id=this._haDeviceId),this._replaceDevice==="none"&&(e.ha_device_id=null)):this._haDeviceId&&(e.ha_device_id=this._haDeviceId);let t=await q(this,e,{busy:i=>{this._loading=i},fallbackKey:"save_error",onError:i=>{this._error=i}});t!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-replaced",{detail:{entry_id:t?.entry_id,device_swap:t?.device_swap}})))}_deviceName(e){let i=this.hass?.devices?.[e];return i?.name_by_user||i?.name||e}_deviceSelector(e){return l`<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${{device:this._haDeviceId||void 0}}
|
||||
.schema=${[{name:"device",selector:{device:{}}}]}
|
||||
@@ -1998,7 +1998,26 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}async _save(){if(this._loading||!this._name.trim())return;this._error="";let e={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},t=await q(this,this._entryId?{type:"maintenance_supporter/object/update",entry_id:this._entryId,...e}:{type:"maintenance_supporter/object/create",...e},{busy:i=>{this._loading=i},fallbackKey:"save_error",onError:i=>{this._error=i}});t!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-saved",{detail:{device_swap:t?.device_swap}})))}_parentChoices(){return(this.objects||[]).filter(e=>e.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return l``;if(this._replacing)return this._renderReplace();let e=this._lang,t=this._entryId?n("edit_object",e):n("new_object",e);return l`
|
||||
`}_renderPlace(e){let t=!!this._place&&!this.hass?.states?.[this._place];return l`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${{place:this._place||void 0}}
|
||||
.schema=${[{name:"place",selector:{entity:{domain:"zone"}}}]}
|
||||
.computeLabel=${()=>n("place_optional",e)}
|
||||
@value-changed=${i=>{this._place=i.detail.value?.place||"",this._place||(this._remindOnSite=!1)}}
|
||||
></ha-form>
|
||||
${t?l`<div class="place-missing">${n("place_missing",e)}</div>`:p}
|
||||
<label class="place-on-site ${this._place?"":"disabled"}">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${this._remindOnSite}
|
||||
?disabled=${!this._place}
|
||||
@change=${i=>this._remindOnSite=i.target.checked}
|
||||
/>
|
||||
<span>${n("remind_on_site",e)}</span>
|
||||
</label>
|
||||
${this._place?l`<div class="place-hint">${n("remind_on_site_hint",e)}</div>`:p}
|
||||
`}async _save(){if(this._loading||!this._name.trim())return;this._error="";let e={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.placesEnabled?{place:this._place||null,remind_on_site:!!this._place&&this._remindOnSite}:{}},t=await q(this,this._entryId?{type:"maintenance_supporter/object/update",entry_id:this._entryId,...e}:{type:"maintenance_supporter/object/create",...e},{busy:i=>{this._loading=i},fallbackKey:"save_error",onError:i=>{this._error=i}});t!==void 0&&(this._open=!1,this.dispatchEvent(new CustomEvent("object-saved",{detail:{device_swap:t?.device_swap}})))}_parentChoices(){return(this.objects||[]).filter(e=>e.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return l``;if(this._replacing)return this._renderReplace();let e=this._lang,t=this._entryId?n("edit_object",e):n("new_object",e);return l`
|
||||
<ha-dialog open @closed=${this._close}>
|
||||
<div class="dialog-title">${t}</div>
|
||||
<div class="content">
|
||||
@@ -2036,6 +2055,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._areaId}
|
||||
@value-changed=${i=>this._areaId=i.detail.value||""}
|
||||
></ha-area-picker>
|
||||
${this.placesEnabled?this._renderPlace(e):p}
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
@@ -2099,7 +2119,26 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}};O.styles=$`
|
||||
`}};L.styles=$`
|
||||
.place-on-site {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.place-on-site.disabled {
|
||||
color: var(--disabled-text-color, var(--secondary-text-color));
|
||||
}
|
||||
.place-hint,
|
||||
.place-missing {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
margin-top: -4px;
|
||||
}
|
||||
.place-missing {
|
||||
color: var(--warning-color, #c77700);
|
||||
}
|
||||
.dialog-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
@@ -2169,9 +2208,9 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.radio-row input {
|
||||
margin: 0;
|
||||
}
|
||||
`,c([f({attribute:!1})],O.prototype,"hass",2),c([f({attribute:!1})],O.prototype,"objects",2),c([u()],O.prototype,"_open",2),c([u()],O.prototype,"_loading",2),c([u()],O.prototype,"_error",2),c([u()],O.prototype,"_name",2),c([u()],O.prototype,"_manufacturer",2),c([u()],O.prototype,"_model",2),c([u()],O.prototype,"_serialNumber",2),c([u()],O.prototype,"_areaId",2),c([u()],O.prototype,"_installationDate",2),c([u()],O.prototype,"_warrantyExpiry",2),c([u()],O.prototype,"_documentationUrl",2),c([u()],O.prototype,"_notes",2),c([u()],O.prototype,"_haDeviceId",2),c([u()],O.prototype,"_parentEntryId",2),c([u()],O.prototype,"_entryId",2),c([u()],O.prototype,"_replacing",2),c([u()],O.prototype,"_oldDeviceId",2),c([u()],O.prototype,"_replaceDevice",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",O)});var bs,En,Sn,Tn=y(()=>{"use strict";bs=["sensor","binary_sensor","number","input_number","input_boolean","switch","climate","vacuum","cover","fan","light","water_heater","humidifier","media_player","weather","air_quality","valve","lawn_mower","lock"],En=["sensor"],Sn=["temperature","humidity","pressure"]});var Ho,qo,ys,An=y(()=>{"use strict";Ho={cleaning:"mdi:broom",inspection:"mdi:magnify",replacement:"mdi:swap-horizontal",calibration:"mdi:tune",service:"mdi:wrench",reading:"mdi:counter",custom:"mdi:wrench-clock"},qo="mdi:wrench-clock",ys=r=>r&&Ho[r]||qo});function In(){return{entityIds:"",type:"threshold",attribute:"",above:"",below:"",equals:"",notEquals:"",forMinutes:"0",targetValue:"",deltaMode:!1,fromState:"",toState:"",targetChanges:"",runtimeHours:"",onStates:"",daysBefore:"0",carry:{}}}function Ln(r){return{type:r.type||"threshold",attribute:r.attribute||"",above:r.trigger_above?.toString()??"",below:r.trigger_below?.toString()??"",equals:r.trigger_equals?.toString()??"",notEquals:r.trigger_not_equals?.toString()??"",forMinutes:r.trigger_for_minutes?.toString()??"0",targetValue:r.trigger_target_value?.toString()??"",deltaMode:r.trigger_delta_mode||!1,fromState:r.trigger_from_state||"",toState:r.trigger_to_state||"",targetChanges:r.trigger_target_changes?.toString()??"",runtimeHours:r.trigger_runtime_hours?.toString()??"",onStates:(r.trigger_on_states||[]).join(", "),daysBefore:r.trigger_days_before?.toString()??"0"}}function Nn(r,s){s.attribute&&(r.attribute=s.attribute);let e=parseInt(s.forMinutes,10);if(s.type==="threshold"){let t=parseFloat(s.above);isNaN(t)||(r.trigger_above=t);let i=parseFloat(s.below);isNaN(i)||(r.trigger_below=i);let a=parseFloat(s.equals);isNaN(a)||(r.trigger_equals=a);let o=parseFloat(s.notEquals);isNaN(o)||(r.trigger_not_equals=o),isNaN(e)||(r.trigger_for_minutes=e)}else if(s.type==="counter"){let t=parseFloat(s.targetValue);isNaN(t)||(r.trigger_target_value=t),r.trigger_delta_mode=s.deltaMode}else if(s.type==="state_change"){s.fromState&&(r.trigger_from_state=s.fromState),s.toState&&(r.trigger_to_state=s.toState);let t=parseInt(s.targetChanges,10);isNaN(t)||(r.trigger_target_changes=t),isNaN(e)||(r.trigger_for_minutes=e)}else if(s.type==="runtime"){let t=parseFloat(s.runtimeHours);isNaN(t)||(r.trigger_runtime_hours=t);let i=(s.onStates||"").split(",").map(a=>a.trim()).filter(Boolean);i.length>0&&(r.trigger_on_states=i)}else if(s.type==="due_date"){let t=parseInt(s.daysBefore,10);r.trigger_days_before=isNaN(t)?0:t}}function Bo(r){return{entityIds:(r.entity_ids||(r.entity_id?[r.entity_id]:[])).join(", "),...Ln(r),carry:Object.fromEntries(Object.entries(r).filter(([e])=>!Uo.has(e)&&!e.startsWith("_")))}}function Wo(r){let s=r.entityIds.split(",").map(t=>t.trim()).filter(Boolean);if(s.length===0)return null;let e={...r.carry||{},entity_id:s[0],entity_ids:s,type:r.type};return Nn(e,r),e}function Vo(r){return Array.from({length:7},(s,e)=>Ct(e,r,"short"))}function Go(r){return Array.from({length:12},(s,e)=>Lr(e,r,"short"))}var Do,Mo,zo,fi,Cn,Pn,Rn,Fo,he,je,Uo,g,xs,On=y(()=>{"use strict";R();j();C();rs();ai();Oe();Tn();ke();Lt();ui();ds();vs();An();rt();hi();Do=["cleaning","inspection","replacement","calibration","service","reading","custom"],Mo=["low","normal","high"],zo=["time_based","weekdays","nth_weekday","day_of_month","calendar","sensor_based","one_time","manual"],fi=["weekdays","nth_weekday","day_of_month","calendar"],Cn=["time_based","one_time",...fi],Pn=["threshold","counter","state_change","runtime","due_date"],Rn=(r,s)=>n(r==="due_date"?"trigger_type_due_date":r,s),Fo=[...Pn,"compound"],he={alpha:"0.3",min:"7",max:"365"},je=we;Uo=new Set(["entity_id","entity_ids","type","attribute","trigger_above","trigger_below","trigger_equals","trigger_not_equals","trigger_for_minutes","trigger_target_value","trigger_delta_mode","trigger_from_state","trigger_to_state","trigger_target_changes","trigger_runtime_hours","trigger_on_states","trigger_days_before"]);g=class g extends E{constructor(){super(...arguments);this.checklistsEnabled=!1;this.scheduleTimeEnabled=!1;this.completionActionsEnabled=!1;this.adaptiveFeature=!1;this.seasonalFeature=
Line truncated
|
||||
`),this._phaseDefs=Object.entries(t.phases||{}).map(([_,m])=>{let{name:b,checklist:k,consumes_parts:w,required_completion_fields:v,...x}=m,T=m.consumes_parts||[],N=T.findIndex(ce=>!ce.entry_id),V=N>=0?T[N]:void 0;return{id:_,name:m.name||_,checklistText:(m.checklist||[]).join(`
|
||||
`),partId:V?.part_id||"",partQty:V?.quantity!=null?String(V.quantity):"",reqOverride:m.required_completion_fields!==void 0,reqFields:[...m.required_completion_fields||[]],extraParts:T.filter((ce,Me)=>Me!==N).map(ce=>({...ce})),carry:x}}),this._phaseSeq=[...t.phase_sequence||[]],this._requiredCompletion=[...t.required_completion_fields||[]],this._scheduleTime=t.schedule_time||"",this._scheduleTimeOn=!!t.schedule_time;let o=t.on_complete_action;if(o&&o.service){this._actionService=o.service;let _=o.target?.entity_id;this._actionTargetEntity=Array.isArray(_)?_[0]||"":_||"",this._actionTargetStored=o.target&&typeof o.target=="object"?JSON.parse(JSON.stringify(o.target)):null,this._actionTargetInitial=this._actionTargetEntity,this._actionData=o.data&&typeof o.data=="object"?{...o.data}:{},this._actionDataJsonFallback="",this._actionSkipAuto=o.skip_auto===!0,this._actionPresent=!0}else this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1;let d=t.quick_complete_defaults;this._qcNotes=d?.notes||"",this._qcCost=d?.cost!=null?String(d.cost):"",this._qcDuration=d?.duration!=null?String(d.duration):"",this._qcFeedback=d?.feedback||"";let h=t.adaptive_config||{};if(this._environmentalEntity=h.environmental_entity||"",this._environmentalAttribute=h.environmental_attribute||"",this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute,this._adaptiveEnabled=!!h.enabled,this._adaptiveAlpha=h.ewa_alpha?.toString()??he.alpha,this._adaptiveMin=h.min_interval_days?.toString()??he.min,this._adaptiveMax=h.max_interval_days?.toString()??he.max,this._adaptiveSeasonal=h.seasonal_enabled!==!1,this._adaptivePrediction=h.sensor_prediction_enabled!==!1,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=this._adaptiveEnabled,t.trigger_config){let _=t.trigger_config;this._triggerEntityId=_.entity_id||_.entity_ids&&_.entity_ids[0]||"",this._triggerEntityIds=_.entity_ids||(_.entity_id?[_.entity_id]:[]),this._triggerEntityLogic=_.entity_logic||"any",this._setTypeFields(Ln(_)),this._triggerCombinator=_.trigger_combinator==="all"?"all":"any",this._triggerBaselineValue=_.trigger_baseline_value?.toString()||"",this._liveBaselineValue=t.trigger_baseline_value??null,this._autoCompleteOnRecovery=_.auto_complete_on_recovery||!1,this._triggerRuntimeMaxSession=_.trigger_runtime_max_session_seconds?.toString()||"",_.type==="compound"?(this._compoundLogic=_.compound_logic==="OR"?"OR":"AND",this._compoundConditions=(_.conditions||[]).map(Bo)):(this._compoundLogic="AND",this._compoundConditions=[])}else this._resetTriggerFields();this._triggerEntityId&&this._fetchEntityAttributes(this._triggerEntityId),await Promise.all([this._loadUsers(),this._loadTags(),this._loadParts(),this._loadForeignPools()]),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._notifyIcon="",this._priority="normal",this._labels="",this._mirrorTodoEntities=[],this._enabled=!0,this._lastPerformed="",this._nfcTagId="",this._requireTagScan=!1,this._allowSkip=!0,this._readingUnit="",this._readings=[],this._consumesParts={},this._consumesQtyText={},this._responsibleUserId=null,this._assigneePool=[],this._rotationStrategy="",this._checklistText="",this._phaseDefs=[],this._phaseSeq=[],this._requiredCompletion=[],this._scheduleTime="",this._scheduleTimeOn=!1,this._environmentalEntity="",this._environmentalAttribute="",this._environmentalInitial="",this._environmentalAttributeInitial="",this._adaptiveEnabled=!1,this._adaptiveAlpha=he.alpha,this._adaptiveMin=he.min,this._adaptiveMax=he.max,this._adaptiveSeasonal=!0,this._adaptivePrediction=!0,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=!1,this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1,this._actionTesting=!1,this._actionTestResult="",this._qcNotes="",this._qcCost="",this._qcDuration="",this._qcFeedback="",this._resetTriggerFields()}_typeFields(){return{type:this._triggerType,attribute:this._triggerAttribute,above:this._triggerAbove,below:this._triggerBelow,equals:this._triggerEquals,notEquals:this._triggerNotEquals,forMinutes:this._triggerForMinutes,targetValue:this._triggerTargetValue,deltaMode:this._triggerDeltaMode,fromState:
Line truncated
|
||||
`,c([f({attribute:!1})],L.prototype,"hass",2),c([f({attribute:!1})],L.prototype,"objects",2),c([f({type:Boolean})],L.prototype,"placesEnabled",2),c([u()],L.prototype,"_open",2),c([u()],L.prototype,"_loading",2),c([u()],L.prototype,"_error",2),c([u()],L.prototype,"_name",2),c([u()],L.prototype,"_manufacturer",2),c([u()],L.prototype,"_model",2),c([u()],L.prototype,"_serialNumber",2),c([u()],L.prototype,"_areaId",2),c([u()],L.prototype,"_installationDate",2),c([u()],L.prototype,"_warrantyExpiry",2),c([u()],L.prototype,"_documentationUrl",2),c([u()],L.prototype,"_notes",2),c([u()],L.prototype,"_haDeviceId",2),c([u()],L.prototype,"_parentEntryId",2),c([u()],L.prototype,"_place",2),c([u()],L.prototype,"_remindOnSite",2),c([u()],L.prototype,"_entryId",2),c([u()],L.prototype,"_replacing",2),c([u()],L.prototype,"_oldDeviceId",2),c([u()],L.prototype,"_replaceDevice",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",L)});var ys,Sn,Tn,An=y(()=>{"use strict";ys=["sensor","binary_sensor","number","input_number","input_boolean","switch","climate","vacuum","cover","fan","light","water_heater","humidifier","media_player","weather","air_quality","valve","lawn_mower","lock"],Sn=["sensor"],Tn=["temperature","humidity","pressure"]});var zo,Fo,xs,Cn=y(()=>{"use strict";zo={cleaning:"mdi:broom",inspection:"mdi:magnify",replacement:"mdi:swap-horizontal",calibration:"mdi:tune",service:"mdi:wrench",reading:"mdi:counter",custom:"mdi:wrench-clock"},Fo="mdi:wrench-clock",xs=r=>r&&zo[r]||Fo});function vi(r){return r?Array.isArray(r.entity_ids)&&r.entity_ids.length>0?[...r.entity_ids]:r.entity_id?[r.entity_id]:[]:[]}function Rn(r){if(!(!r||r.type==="compound"))return vi(r)[0]}var In=y(()=>{"use strict"});function Nn(){return{entityIds:"",type:"threshold",attribute:"",above:"",below:"",equals:"",notEquals:"",forMinutes:"0",targetValue:"",deltaMode:!1,fromState:"",toState:"",targetChanges:"",runtimeHours:"",onStates:"",daysBefore:"0",carry:{}}}function Hn(r){return{type:r.type||"threshold",attribute:r.attribute||"",above:r.trigger_above?.toString()??"",below:r.trigger_below?.toString()??"",equals:r.trigger_equals?.toString()??"",notEquals:r.trigger_not_equals?.toString()??"",forMinutes:r.trigger_for_minutes?.toString()??"0",targetValue:r.trigger_target_value?.toString()??"",deltaMode:r.trigger_delta_mode||!1,fromState:r.trigger_from_state||"",toState:r.trigger_to_state||"",targetChanges:r.trigger_target_changes?.toString()??"",runtimeHours:r.trigger_runtime_hours?.toString()??"",onStates:(r.trigger_on_states||[]).join(", "),daysBefore:r.trigger_days_before?.toString()??"0"}}function jn(r,s){s.attribute&&(r.attribute=s.attribute);let e=parseInt(s.forMinutes,10);if(s.type==="threshold"){let t=parseFloat(s.above);isNaN(t)||(r.trigger_above=t);let i=parseFloat(s.below);isNaN(i)||(r.trigger_below=i);let a=parseFloat(s.equals);isNaN(a)||(r.trigger_equals=a);let o=parseFloat(s.notEquals);isNaN(o)||(r.trigger_not_equals=o),isNaN(e)||(r.trigger_for_minutes=e)}else if(s.type==="counter"){let t=parseFloat(s.targetValue);isNaN(t)||(r.trigger_target_value=t),r.trigger_delta_mode=s.deltaMode}else if(s.type==="state_change"){s.fromState&&(r.trigger_from_state=s.fromState),s.toState&&(r.trigger_to_state=s.toState);let t=parseInt(s.targetChanges,10);isNaN(t)||(r.trigger_target_changes=t),isNaN(e)||(r.trigger_for_minutes=e)}else if(s.type==="runtime"){let t=parseFloat(s.runtimeHours);isNaN(t)||(r.trigger_runtime_hours=t);let i=(s.onStates||"").split(",").map(a=>a.trim()).filter(Boolean);i.length>0&&(r.trigger_on_states=i)}else if(s.type==="due_date"){let t=parseInt(s.daysBefore,10);r.trigger_days_before=isNaN(t)?0:t}}function Ko(r){return{entityIds:vi(r).join(", "),...Hn(r),carry:Object.fromEntries(Object.entries(r).filter(([e])=>!Go.has(e)&&!e.startsWith("_")))}}function Yo(r){let s=r.entityIds.split(",").map(t=>t.trim()).filter(Boolean);if(s.length===0)return null;let e={...r.carry||{},entity_id:s[0],entity_ids:s,type:r.type};return jn(e,r),e}function Qo(r){return Array.from({length:7},(s,e)=>Ct(e,r,"short"))}function Zo(r){return Array.from({length:12},(s,e)=>Nr(e,r,"short"))}var Uo,Bo,Wo,bi,Pn,On,Ln,Vo,he,He,Go,g,ws,qn=y(()=>{"use strict";R();H();C();ns();oi();Oe();An();ke();Lt();hi();ps();bs();Cn();In();rt();_i();Uo=["cleaning","inspection","replacement","calibration","service","reading","custom"],Bo=["low","normal","high"],Wo=["time_based","weekdays","nth_weekday","day_of_month","calendar","sensor_based","one_time","manual"],bi=["weekdays","nth_weekday","day_of_month","calendar"],Pn=["time_based","one_time",...bi],On=["threshold","counter","state_change","runtime","due_date"],Ln=(r,s)=>n(r==="due_date"?"trigger_type_due_date":r,s),Vo=[...On,"compound"],he={alpha:"0.3",min:"7",max:"365"},He=we;Go=new Set(["entity_id","entity_ids","type","attribute","trigger_above","trigger_below","trigger_equals","trigger_not_equals","trigger_for_minutes","trigger_target_value","trigger_delta_mode","trigger_from_st
Line truncated
|
||||
`),this._phaseDefs=Object.entries(t.phases||{}).map(([_,m])=>{let{name:b,checklist:k,consumes_parts:w,required_completion_fields:v,...x}=m,T=m.consumes_parts||[],O=T.findIndex(ce=>!ce.entry_id),G=O>=0?T[O]:void 0;return{id:_,name:m.name||_,checklistText:(m.checklist||[]).join(`
|
||||
`),partId:G?.part_id||"",partQty:G?.quantity!=null?String(G.quantity):"",reqOverride:m.required_completion_fields!==void 0,reqFields:[...m.required_completion_fields||[]],extraParts:T.filter((ce,Me)=>Me!==O).map(ce=>({...ce})),carry:x}}),this._phaseSeq=[...t.phase_sequence||[]],this._requiredCompletion=[...t.required_completion_fields||[]],this._scheduleTime=t.schedule_time||"",this._scheduleTimeOn=!!t.schedule_time;let o=t.on_complete_action;if(o&&o.service){this._actionService=o.service;let _=o.target?.entity_id;this._actionTargetEntity=Array.isArray(_)?_[0]||"":_||"",this._actionTargetStored=o.target&&typeof o.target=="object"?JSON.parse(JSON.stringify(o.target)):null,this._actionTargetInitial=this._actionTargetEntity,this._actionData=o.data&&typeof o.data=="object"?{...o.data}:{},this._actionDataJsonFallback="",this._actionSkipAuto=o.skip_auto===!0,this._actionPresent=!0}else this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1;let d=t.quick_complete_defaults;this._qcNotes=d?.notes||"",this._qcCost=d?.cost!=null?String(d.cost):"",this._qcDuration=d?.duration!=null?String(d.duration):"",this._qcFeedback=d?.feedback||"";let h=t.adaptive_config||{};if(this._environmentalEntity=h.environmental_entity||"",this._environmentalAttribute=h.environmental_attribute||"",this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute,this._adaptiveEnabled=!!h.enabled,this._adaptiveAlpha=h.ewa_alpha?.toString()??he.alpha,this._adaptiveMin=h.min_interval_days?.toString()??he.min,this._adaptiveMax=h.max_interval_days?.toString()??he.max,this._adaptiveSeasonal=h.seasonal_enabled!==!1,this._adaptivePrediction=h.sensor_prediction_enabled!==!1,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=this._adaptiveEnabled,t.trigger_config){let _=t.trigger_config;this._triggerEntityId=Rn(_)||"",this._triggerEntityIds=vi(_),this._triggerEntityLogic=_.entity_logic||"any",this._setTypeFields(Hn(_)),this._triggerCombinator=_.trigger_combinator==="all"?"all":"any",this._triggerBaselineValue=_.trigger_baseline_value?.toString()||"",this._liveBaselineValue=t.trigger_baseline_value??null,this._autoCompleteOnRecovery=_.auto_complete_on_recovery||!1,this._triggerRuntimeMaxSession=_.trigger_runtime_max_session_seconds?.toString()||"",_.type==="compound"?(this._compoundLogic=_.compound_logic==="OR"?"OR":"AND",this._compoundConditions=(_.conditions||[]).map(Ko)):(this._compoundLogic="AND",this._compoundConditions=[])}else this._resetTriggerFields();this._triggerEntityId&&this._fetchEntityAttributes(this._triggerEntityId),await Promise.all([this._loadUsers(),this._loadTags(),this._loadParts(),this._loadForeignPools()]),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._notifyIcon="",this._priority="normal",this._labels="",this._mirrorTodoEntities=[],this._enabled=!0,this._lastPerformed="",this._nfcTagId="",this._requireTagScan=!1,this._allowSkip=!0,this._readingUnit="",this._readings=[],this._consumesParts={},this._consumesQtyText={},this._responsibleUserId=null,this._assigneePool=[],this._rotationStrategy="",this._checklistText="",this._phaseDefs=[],this._phaseSeq=[],this._requiredCompletion=[],this._scheduleTime="",this._scheduleTimeOn=!1,this._environmentalEntity="",this._environmentalAttribute="",this._environmentalInitial="",this._environmentalAttributeInitial="",this._adaptiveEnabled=!1,this._adaptiveAlpha=he.alpha,this._adaptiveMin=he.min,this._adaptiveMax=he.max,this._adaptiveSeasonal=!0,this._adaptivePrediction=!0,this._adaptiveInitial=this._adaptiveSnapshot(),this._adaptiveWasEnabled=!1,this._actionService="",this._actionTargetEntity="",this._actionTargetStored=null,this._actionTargetInitial="",this._actionData={},this._actionDataJsonFallback="",this._actionSkipAuto=!1,this._actionPresent=!1,this._actionTesting=!1,this._actionTestResult="",this._qcNotes="",this._qcCost="",this._qcDuration="",this._qcFeedback="",this._resetTriggerFields()}_typeFields(){return{type:this._triggerType,attribute:this._triggerAttribute,above:this._triggerAbove,below:this._triggerBelow,equals:this._triggerEquals,notEquals:this._triggerNotEquals,forMinutes:this._triggerForMinutes,targetValue:this._triggerTargetValue,deltaMode:this._triggerDeltaMode,fromState:this._triggerFromState,toState:this._triggerToState,targetChanges:this._trig
Line truncated
|
||||
<details class="ca-section">
|
||||
<summary>${n("on_complete_action_title",e)}</summary>
|
||||
<p class="field-help">${n("on_complete_action_desc",e)}</p>
|
||||
@@ -2266,14 +2305,14 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
${a!==void 0?l`<input
|
||||
class="consumes-qty"
|
||||
type="number"
|
||||
min=${je[0]}
|
||||
max=${je[1]}
|
||||
min=${He[0]}
|
||||
max=${He[1]}
|
||||
step="0.01"
|
||||
.value=${this._consumesQtyText[i]??String(a.quantity)}
|
||||
@input=${d=>{let h=d.target.value;this._consumesQtyText={...this._consumesQtyText,[i]:h};let _=parseFloat(h.replace(",","."));Number.isFinite(_)&&_>=je[0]&&(this._consumesParts={...this._consumesParts,[i]:{...o,quantity:_}})}}
|
||||
@input=${d=>{let h=d.target.value;this._consumesQtyText={...this._consumesQtyText,[i]:h};let _=parseFloat(h.replace(",","."));Number.isFinite(_)&&_>=He[0]&&(this._consumesParts={...this._consumesParts,[i]:{...o,quantity:_}})}}
|
||||
/>`:p}
|
||||
</div>
|
||||
`}_applyConsumesQtyText(){let e={...this._consumesParts};for(let t of Object.keys(e)){let i=this._consumesQtyText[t];if(i===void 0)continue;let a=parseFloat(i.replace(",","."));if(!Number.isFinite(a)||a<je[0]||a>je[1]){let o=d=>I(d,this._lang,{maximumFractionDigits:2});return this._error=n("settings_value_out_of_range",this._lang).replace("{min}",o(je[0])).replace("{max}",o(je[1])),!1}e[t]={...e[t],quantity:a}}return this._consumesParts=e,!0}_toggleRequired(e,t){let i=new Set(this._requiredCompletion);t?i.add(e):i.delete(e),this._requiredCompletion=[...i]}_patchReading(e,t){this._readings=this._readings.map(i=>i.id===e?{...i,...t}:i)}_renderReadingsEditor(e){let t=Kr(this._readings);return l`
|
||||
`}_applyConsumesQtyText(){let e={...this._consumesParts};for(let t of Object.keys(e)){let i=this._consumesQtyText[t];if(i===void 0)continue;let a=parseFloat(i.replace(",","."));if(!Number.isFinite(a)||a<He[0]||a>He[1]){let o=d=>I(d,this._lang,{maximumFractionDigits:2});return this._error=n("settings_value_out_of_range",this._lang).replace("{min}",o(He[0])).replace("{max}",o(He[1])),!1}e[t]={...e[t],quantity:a}}return this._consumesParts=e,!0}_toggleRequired(e,t){let i=new Set(this._requiredCompletion);t?i.add(e):i.delete(e),this._requiredCompletion=[...i]}_patchReading(e,t){this._readings=this._readings.map(i=>i.id===e?{...i,...t}:i)}_renderReadingsEditor(e){let t=Yr(this._readings);return l`
|
||||
<div class="readings-editor">
|
||||
<div class="field-label">${n("readings_section",e)}</div>
|
||||
<div class="field-help">${n("readings_hint",e)}</div>
|
||||
@@ -2297,9 +2336,9 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
`)}
|
||||
${this._readings.length<as?l`
|
||||
${this._readings.length<os?l`
|
||||
<ha-button appearance="plain" class="reading-add"
|
||||
@click=${()=>this._readings=[...this._readings,{id:Ur(),name:"",unit:this._readings.length?this._readings[this._readings.length-1].unit:this._readingUnit}]}>
|
||||
@click=${()=>this._readings=[...this._readings,{id:Br(),name:"",unit:this._readings.length?this._readings[this._readings.length-1].unit:this._readingUnit}]}>
|
||||
<ha-icon icon="mdi:plus"></ha-icon> ${n("reading_add",e)}
|
||||
</ha-button>`:p}
|
||||
</div>
|
||||
@@ -2354,7 +2393,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</label>
|
||||
${i.reqOverride?l`
|
||||
<div class="required-completion phase-req-fields">
|
||||
${cs.map(a=>l`
|
||||
${ds.map(a=>l`
|
||||
<label class="req-option">
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -2390,8 +2429,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</select>
|
||||
</div>
|
||||
`:p}
|
||||
`}async _save(){if(!this._loading&&this._name.trim()){if(this._adaptiveSnapshot()!==this._adaptiveInitial){let e=parseInt(this._adaptiveMin,10),t=parseInt(this._adaptiveMax,10);if(!isNaN(e)&&!isNaN(t)&&e>t){this._error=`${n("adaptive_min_interval",this._lang)} > ${n("adaptive_max_interval",this._lang)}`;return}}if(this._triggerType==="threshold"&&this._thresholdLimitsOverlap()){this._error=n("trigger_hint_overlap",this._lang);return}if(this._scheduleType==="calendar"&&!this._calendarEntity.trim().startsWith("calendar.")){this._error=n("calendar_entity_required",this._lang);return}if(this._applyConsumesQtyText()){this._loading=!0,this._error="";try{let e=await q(this,()=>this.hass.connection.sendMessagePromise(this._savePayload()),{fallbackKey:"save_error",onError:o=>{this._error=o}});if(e===void 0)return;let t=this._taskId||e?.task_id,i=this._environmentalEntity!==this._environmentalInitial||this._environmentalAttribute!==this._environmentalAttributeInitial;this._warning="";let a=o=>{this._warning=n("subsave_warning",this._lang).replace("{detail}",o)};if(t&&this._scheduleType==="sensor_based"&&i&&await q(this,{type:"maintenance_supporter/task/set_environmental_entity",entry_id:this._entryId,task_id:t,environmental_entity:this._environmentalEntity||null,environmental_attribute:this._environmentalAttribute||null},{onError:a})!==void 0&&(this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute),t&&this._adaptiveSnapshot()!==this._adaptiveInitial){let o=parseFloat(this._adaptiveAlpha),d=parseInt(this._adaptiveMin,10),h=parseInt(this._adaptiveMax,10);await q(this,{type:"maintenance_supporter/task/set_adaptive",entry_id:this._entryId,task_id:t,enabled:this._adaptiveEnabled,...o>=Pt[0]&&o<=Pt[1]?{ewa_alpha:o}:{},...!isNaN(d)&&d>=1?{min_interval_days:d}:{},...!isNaN(h)&&h>=1?{max_interval_days:h}:{},seasonal_enabled:this._adaptiveSeasonal,sensor_prediction_enabled:this._adaptivePrediction},{onError:a})!==void 0&&(this._adaptiveInitial=this._adaptiveSnapshot())}this._warning?t&&(this._taskId=t):this._open=!1,this.dispatchEvent(new CustomEvent("task-saved"))}finally{this._loading=!1}}}}_savePayload(){let e={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:Number.isNaN(parseInt(this._warningDays,10))?this.defaultWarningDays:Math.max(0,parseInt(this._warningDays,10))},t=this._earliestCompletionDays.trim();e.earliest_completion_days=t===""?null:Math.max(0,parseInt(t,10)||0),this._taskId&&(e.task_id=this._taskId),this._scheduleType==="one_time"?(e.due_date=this._dueDate||null,e.interval_days=null):fi.includes(this._scheduleType)?(e.schedule={...this._buildSchedule(),...this._recurrenceExtras()},e.interval_days=null,this._taskId&&(e.due_date=null)):(this._taskId&&(e.due_date=null),this._scheduleType!=="manual"&&this._intervalDays?(e.interval_days=parseInt(this._intervalDays,10),e.interval_unit=this._intervalUnit,e.interval_anchor=this._intervalAnchor,this._scheduleType==="time_based"&&(e.schedule={kind:"interval",...this._recurrenceExtras()})):this._taskId&&(e.interval_days=null,e.interval_anchor="completion")),e.notes=this._notes||null,e.documentation_url=this._documentationUrl||null,e.custom_icon=this._customIcon||null,e.notify_icon=this._notifyIcon.trim()||null,e.priority=this._priority,e.labels=this._labels.split(",").map(i=>i.trim()).filter(Boolean),e.mirror_todo_entities=this._mirrorTodoEntities.filter(Boolean),e.enabled=this._enabled,(!this._taskId||this._lastPerformed!==this._loadedLastPerformed)&&(e.last_performed=this._lastPerformed||null),e.nfc_tag_id=this._nfcTagId||null,e.require_tag_scan=this._requireTagScan,e.allow_skip=this._allowSkip,e.notify_enabled=this._notifyEnabled,e.reading_unit=this._readingUnit.trim()||null,e.readings=Yr(this._readings);{let i={};for(let o of this._phaseDefs){if(!o.name.trim())continue;let d={...o.carry,name:o.name.trim()},h=o.checklistText.split(`
|
||||
`).map(m=>m.trim()).filter(Boolean);h.length&&(d.checklist=h);let _=[];if(o.partId){let m=parseFloat(o.partQty);_.push({part_id:o.partId,quantity:Number.isFinite(m)&&m>0?m:1})}for(let m of o.extraParts)_.push(m.entry_id?{part_id:m.part_id,quantity:m.quantity,entry_id:m.entry_id}:{part_id:m.part_id,quantity:m.quantity});_.length&&(d.consumes_parts=_),o.reqOverride&&(d.required_completion_fields=[...o.reqFields]),i[o.id]=d}let a=this._phaseSeq.filter(o=>o in i);e.phases=Object.keys(i).length&&a.length?i:null,e.phase_sequence=e.phases?a:null}if((this.parts.length||this._foreignOwners.length)&&(e.consumes_parts=Object.values(this._consumesParts).map(i=>i.entry_id?{part_id:i.part_id,quantity:i.quantity,entry_id:i.entry_id}:{part_id:i.part_id,quantity:i.quantity})),(!this._taskId||this._responsibleUserId!==this._loadedResponsibleUserId)&&(e.responsible_user_id=this._responsibleUserId),e.assignee_pool=this._assigneePool,e.required_completion_fields=this._requiredCompletion,e.rotation_strategy=this._assigneePool.length>=2&&this._rotationStrategy?this._rotationStrategy:null,this._scheduleType==="sensor_based"&&this._triggerType==="compound"){let i=this._compoundConditions.map(Wo).filter(a=>a!==null);if(i.length>0){let a={type:"compound",compound_logic:this._compoundLogic,conditions:i};this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),e.trigger_config=a}else this._taskId&&(e.trigger_config=null)}else if(this._scheduleType==="sensor_based"&&this._triggerEntityId){let i=this._triggerEntityIds.length>0?this._triggerEntityIds:[this._triggerEntityId],a={entity_id:i[0],entity_ids:i,type:this._triggerType};if(Nn(a,this._typeFields()),this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),i.length>1&&(a.entity_logic=this._triggerEntityLogic),this._triggerType==="counter"&&this._triggerDeltaMode&&this._triggerBaselineValue){let o=parseFloat(this._triggerBaselineValue);!isNaN(o)&&o>=0&&(a.trigger_baseline_value=o)}else if(this._triggerType==="runtime"&&this._triggerRuntimeMaxSession){let o=parseInt(this._triggerRuntimeMaxSession,10);!isNaN(o)&&o>0&&(a.trigger_runtime_max_session_seconds=o)}e.trigger_config=a}else this._taskId&&(e.trigger_config=null);if(this.scheduleTimeEnabled&&Cn.includes(this._scheduleType)){let i=this._scheduleTimeOn?this._scheduleTime.trim():"";e.schedule_time=/^([01]\d|2[0-3]):[0-5]\d$/.test(i)?i:null}if(this.checklistsEnabled){let i=this._checklistText.split(`
|
||||
`}async _save(){if(!this._loading&&this._name.trim()){if(this._adaptiveSnapshot()!==this._adaptiveInitial){let e=parseInt(this._adaptiveMin,10),t=parseInt(this._adaptiveMax,10);if(!isNaN(e)&&!isNaN(t)&&e>t){this._error=`${n("adaptive_min_interval",this._lang)} > ${n("adaptive_max_interval",this._lang)}`;return}}if(this._triggerType==="threshold"&&this._thresholdLimitsOverlap()){this._error=n("trigger_hint_overlap",this._lang);return}if(this._scheduleType==="calendar"&&!this._calendarEntity.trim().startsWith("calendar.")){this._error=n("calendar_entity_required",this._lang);return}if(this._applyConsumesQtyText()){this._loading=!0,this._error="";try{let e=await q(this,()=>this.hass.connection.sendMessagePromise(this._savePayload()),{fallbackKey:"save_error",onError:o=>{this._error=o}});if(e===void 0)return;let t=this._taskId||e?.task_id,i=this._environmentalEntity!==this._environmentalInitial||this._environmentalAttribute!==this._environmentalAttributeInitial;this._warning="";let a=o=>{this._warning=n("subsave_warning",this._lang).replace("{detail}",o)};if(t&&this._scheduleType==="sensor_based"&&i&&await q(this,{type:"maintenance_supporter/task/set_environmental_entity",entry_id:this._entryId,task_id:t,environmental_entity:this._environmentalEntity||null,environmental_attribute:this._environmentalAttribute||null},{onError:a})!==void 0&&(this._environmentalInitial=this._environmentalEntity,this._environmentalAttributeInitial=this._environmentalAttribute),t&&this._adaptiveSnapshot()!==this._adaptiveInitial){let o=parseFloat(this._adaptiveAlpha),d=parseInt(this._adaptiveMin,10),h=parseInt(this._adaptiveMax,10);await q(this,{type:"maintenance_supporter/task/set_adaptive",entry_id:this._entryId,task_id:t,enabled:this._adaptiveEnabled,...o>=Pt[0]&&o<=Pt[1]?{ewa_alpha:o}:{},...!isNaN(d)&&d>=1?{min_interval_days:d}:{},...!isNaN(h)&&h>=1?{max_interval_days:h}:{},seasonal_enabled:this._adaptiveSeasonal,sensor_prediction_enabled:this._adaptivePrediction},{onError:a})!==void 0&&(this._adaptiveInitial=this._adaptiveSnapshot())}this._warning?t&&(this._taskId=t):this._open=!1,this.dispatchEvent(new CustomEvent("task-saved"))}finally{this._loading=!1}}}}_savePayload(){let e={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:Number.isNaN(parseInt(this._warningDays,10))?this.defaultWarningDays:Math.max(0,parseInt(this._warningDays,10))},t=this._earliestCompletionDays.trim();e.earliest_completion_days=t===""?null:Math.max(0,parseInt(t,10)||0),this._taskId&&(e.task_id=this._taskId),this._scheduleType==="one_time"?(e.due_date=this._dueDate||null,e.interval_days=null):bi.includes(this._scheduleType)?(e.schedule={...this._buildSchedule(),...this._recurrenceExtras()},e.interval_days=null,this._taskId&&(e.due_date=null)):(this._taskId&&(e.due_date=null),this._scheduleType!=="manual"&&this._intervalDays?(e.interval_days=parseInt(this._intervalDays,10),e.interval_unit=this._intervalUnit,e.interval_anchor=this._intervalAnchor,this._scheduleType==="time_based"&&(e.schedule={kind:"interval",...this._recurrenceExtras()})):this._taskId&&(e.interval_days=null,e.interval_anchor="completion")),e.notes=this._notes||null,e.documentation_url=this._documentationUrl||null,e.custom_icon=this._customIcon||null,e.notify_icon=this._notifyIcon.trim()||null,e.priority=this._priority,e.labels=this._labels.split(",").map(i=>i.trim()).filter(Boolean),e.mirror_todo_entities=this._mirrorTodoEntities.filter(Boolean),e.enabled=this._enabled,(!this._taskId||this._lastPerformed!==this._loadedLastPerformed)&&(e.last_performed=this._lastPerformed||null),e.nfc_tag_id=this._nfcTagId||null,e.require_tag_scan=this._requireTagScan,e.allow_skip=this._allowSkip,e.notify_enabled=this._notifyEnabled,e.reading_unit=this._readingUnit.trim()||null,e.readings=Qr(this._readings);{let i={};for(let o of this._phaseDefs){if(!o.name.trim())continue;let d={...o.carry,name:o.name.trim()},h=o.checklistText.split(`
|
||||
`).map(m=>m.trim()).filter(Boolean);h.length&&(d.checklist=h);let _=[];if(o.partId){let m=parseFloat(o.partQty);_.push({part_id:o.partId,quantity:Number.isFinite(m)&&m>0?m:1})}for(let m of o.extraParts)_.push(m.entry_id?{part_id:m.part_id,quantity:m.quantity,entry_id:m.entry_id}:{part_id:m.part_id,quantity:m.quantity});_.length&&(d.consumes_parts=_),o.reqOverride&&(d.required_completion_fields=[...o.reqFields]),i[o.id]=d}let a=this._phaseSeq.filter(o=>o in i);e.phases=Object.keys(i).length&&a.length?i:null,e.phase_sequence=e.phases?a:null}if((this.parts.length||this._foreignOwners.length)&&(e.consumes_parts=Object.values(this._consumesParts).map(i=>i.entry_id?{part_id:i.part_id,quantity:i.quantity,entry_id:i.entry_id}:{part_id:i.part_id,quantity:i.quantity})),(!this._taskId||this._responsibleUserId!==this._loadedResponsibleUserId)&&(e.responsible_user_id=this._responsibleUserId),e.assignee_pool=this._assigneePool,e.required_completion_fields=this._requiredCompletion,e.rotation_strategy=this._assigneePool.length>=2&&this._rotationStrategy?this._rotationStrategy:null,this._scheduleType==="sensor_based"&&this._triggerType==="compound"){let i=this._compoundConditions.map(Yo).filter(a=>a!==null);if(i.length>0){let a={type:"compound",compound_logic:this._compoundLogic,conditions:i};this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),e.trigger_config=a}else this._taskId&&(e.trigger_config=null)}else if(this._scheduleType==="sensor_based"&&this._triggerEntityId){let i=this._triggerEntityIds.length>0?this._triggerEntityIds:[this._triggerEntityId],a={entity_id:i[0],entity_ids:i,type:this._triggerType};if(jn(a,this._typeFields()),this._autoCompleteOnRecovery&&(a.auto_complete_on_recovery=!0),this._triggerCombinator==="all"&&(a.trigger_combinator="all"),i.length>1&&(a.entity_logic=this._triggerEntityLogic),this._triggerType==="counter"&&this._triggerDeltaMode&&this._triggerBaselineValue){let o=parseFloat(this._triggerBaselineValue);!isNaN(o)&&o>=0&&(a.trigger_baseline_value=o)}else if(this._triggerType==="runtime"&&this._triggerRuntimeMaxSession){let o=parseInt(this._triggerRuntimeMaxSession,10);!isNaN(o)&&o>0&&(a.trigger_runtime_max_session_seconds=o)}e.trigger_config=a}else this._taskId&&(e.trigger_config=null);if(this.scheduleTimeEnabled&&Pn.includes(this._scheduleType)){let i=this._scheduleTimeOn?this._scheduleTime.trim():"";e.schedule_time=/^([01]\d|2[0-3]):[0-5]\d$/.test(i)?i:null}if(this.checklistsEnabled){let i=this._checklistText.split(`
|
||||
`).map(a=>a.trim()).filter(Boolean).slice(0,100);e.checklist=i.length?i:null}if(this.completionActionsEnabled||this._actionPresent){let i=this._actionService.trim();if(i&&/^[a-z][a-z0-9_]*\.[a-z0-9_]+$/.test(i)){let a={service:i},o=this._actionTargetEntity.trim();this._actionTargetStored&&o===this._actionTargetInitial?a.target=JSON.parse(JSON.stringify(this._actionTargetStored)):o&&(a.target={entity_id:o});let d=this._buildActionData();Object.keys(d).length>0&&(a.data=d),this._actionSkipAuto&&(a.skip_auto=!0),e.on_complete_action=a}else e.on_complete_action=null}if(this.completionActionsEnabled){let i={};this._qcNotes.trim()&&(i.notes=this._qcNotes.trim());let a=parseFloat(this._qcCost);isNaN(a)||(i.cost=a);let o=it(this._qcDuration);o!==null&&(i.duration=o),this._qcFeedback&&(i.feedback=this._qcFeedback),e.quick_complete_defaults=Object.keys(i).length?i:null}return e}_close(){this._open=!1,this._warning="",this._pickerProbeTimer!==void 0&&(clearTimeout(this._pickerProbeTimer),this._pickerProbeTimer=void 0),this._pickerProbeStrikes=0}_renderTriggerFields(){if(this._scheduleType!=="sensor_based")return p;let e=this._lang,t=this._triggerType==="compound";return l`
|
||||
<h3>${n("trigger_configuration",e)}</h3>
|
||||
<div class="select-row">
|
||||
@@ -2400,7 +2439,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._triggerType}
|
||||
@change=${i=>this._triggerType=i.target.value}
|
||||
>
|
||||
${Fo.map(i=>l`<option value=${i} ?selected=${i===this._triggerType}>${Rn(i,e)}</option>`)}
|
||||
${Vo.map(i=>l`<option value=${i} ?selected=${i===this._triggerType}>${Ln(i,e)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
${t?this._renderCompoundEditor():l`
|
||||
@@ -2414,7 +2453,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ha-form
|
||||
class="entity-picker-form"
|
||||
.hass=${this.hass}
|
||||
.schema=${[{name:"trigger_entities",selector:{entity:{multiple:!0,domain:bs}}}]}
|
||||
.schema=${[{name:"trigger_entities",selector:{entity:{multiple:!0,domain:ys}}}]}
|
||||
.data=${{trigger_entities:this._triggerEntityIds.length>0?this._triggerEntityIds:this._triggerEntityId?[this._triggerEntityId]:[]}}
|
||||
.computeLabel=${()=>n("entity_id",e)}
|
||||
@value-changed=${i=>{let a=(i.detail.value.trigger_entities||[]).filter(Boolean);this._triggerEntityId=a[0]||"",this._triggerEntityIds=a,a[0]?this._fetchEntityAttributes(a[0]):this._fetchEntityAttributes("")}}
|
||||
@@ -2462,7 +2501,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
</select>
|
||||
</div>
|
||||
`:p}
|
||||
`}_patchCondition(e,t){this._compoundConditions=this._compoundConditions.map((i,a)=>a===e?{...i,...t}:i)}_addCondition(){this._compoundConditions=[...this._compoundConditions,In()]}_removeCondition(e){this._compoundConditions=this._compoundConditions.filter((t,i)=>i!==e)}_renderCompoundEditor(){let e=this._lang;return l`
|
||||
`}_patchCondition(e,t){this._compoundConditions=this._compoundConditions.map((i,a)=>a===e?{...i,...t}:i)}_addCondition(){this._compoundConditions=[...this._compoundConditions,Nn()]}_removeCondition(e){this._compoundConditions=this._compoundConditions.filter((t,i)=>i!==e)}_renderCompoundEditor(){let e=this._lang;return l`
|
||||
<div class="select-row">
|
||||
<label>${n("compound_logic",e)}</label>
|
||||
<select
|
||||
@@ -2499,7 +2538,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ha-form
|
||||
class="entity-picker-form"
|
||||
.hass=${this.hass}
|
||||
.schema=${[{name:"condition_entities",selector:{entity:{multiple:!0,domain:bs}}}]}
|
||||
.schema=${[{name:"condition_entities",selector:{entity:{multiple:!0,domain:ys}}}]}
|
||||
.data=${{condition_entities:e.entityIds.split(",").map(o=>o.trim()).filter(Boolean)}}
|
||||
.computeLabel=${()=>n("entity_id",i)}
|
||||
@value-changed=${o=>{let d=(o.detail.value.condition_entities||[]).filter(Boolean);this._patchCondition(t,{entityIds:d.join(", ")})}}
|
||||
@@ -2511,7 +2550,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${e.type}
|
||||
@change=${o=>this._patchCondition(t,{type:o.target.value})}
|
||||
>
|
||||
${Pn.map(o=>l`<option value=${o} ?selected=${o===e.type}>${Rn(o,i)}</option>`)}
|
||||
${On.map(o=>l`<option value=${o} ?selected=${o===e.type}>${Ln(o,i)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
${this._renderConditionTypeFields(e,t)}
|
||||
@@ -2661,12 +2700,12 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
>
|
||||
${["days","weeks","months","years"].map(t=>l`<option value=${t} ?selected=${t===this._intervalUnit}>${n("unit_"+t,e)}</option>`)}
|
||||
</select>
|
||||
</div>`}_toggleWeekday(e){this._weekdays=this._weekdays.includes(e)?this._weekdays.filter(t=>t!==e):[...this._weekdays,e]}_previewScheduleDict(){if(this._scheduleType==="one_time")return this._dueDate?{kind:"one_time",due_date:this._dueDate}:null;if(this._scheduleType==="calendar"&&!this._calendarEntity.trim())return null;if(fi.includes(this._scheduleType))return{...this._buildSchedule(),...this._recurrenceExtras()};let e=parseInt(this._intervalDays,10);return this._scheduleType==="manual"||!e||e<=0?null:{kind:"interval",every:e,unit:this._intervalUnit,anchor:this._intervalAnchor,...this._recurrenceExtras()}}updated(e){super.updated?.(e),this._scheduleEntityPickerProbe();for(let t of e.keys())if(g._PREVIEW_RELEVANT.has(String(t))){this._schedulePreviewRefresh();return}}_scheduleEntityPickerProbe(){this._entityPickerFallback||this._pickerProbeTimer!==void 0||!this._open||this._scheduleType!=="sensor_based"||(this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),1500))}_probeEntityPickers(){if(this._pickerProbeTimer=void 0,this._entityPickerFallback||!this._open)return;let e=this.shadowRoot?.querySelector("ha-form.entity-picker-form"),t=(this.shadowRoot?.querySelector(".content")?.offsetHeight??0)>0;if(!e||!t){this._pickerProbeStrikes=0;return}let i=(h,_,m=0)=>{if(!(!h||m>10)){(h.tagName?.toLowerCase()??"")==="ha-entity-picker"&&_.push(h);for(let b of[h.shadowRoot,h])if(b)for(let k of Array.from(b.children??[]))i(k,_,m+1)}},a=[...this.shadowRoot?.querySelectorAll("ha-form.entity-picker-form")??[]],o=[];for(let h of a)i(h,o);let d=o.length===0||o.some(h=>h.offsetHeight===0);if(e.offsetHeight===0||d){if(this._pickerProbeStrikes+=1,this._pickerProbeStrikes>=2){this._entityPickerFallback=!0;return}this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),700)}else this._pickerProbeStrikes=0}_schedulePreviewRefresh(){this._previewTimer&&clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._fetchSchedulePreview()},300)}async _fetchSchedulePreview(){let e=this._open?this._previewScheduleDict():null;if(!e){this._schedulePreview=[],this._schedulePreviewEnded=!1;return}let t=++this._previewSeq;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/schedule/preview",schedule:e,...this._lastPerformed?{last_performed:this._lastPerformed}:{}});if(t!==this._previewSeq)return;this._schedulePreview=i.occurrences||[],this._schedulePreviewEnded=!!i.series_ended}catch{}}_renderSchedulePreview(){if(this._schedulePreview.length===0)return p;let e=this._lang,t=this.scheduleTimeEnabled&&this._scheduleTimeOn&&this._scheduleTime?` ${this._scheduleTime}`:"",i=this._schedulePreview.map((o,d)=>{let h=new Date(`${o}T12:00:00`).getDay();return`${Ct(h===0?6:h-1,e,"short")} ${H(o,e)}${d===0?t:""}`}).join(" \xB7 "),a=this._scheduleType==="time_based"&&this._intervalAnchor==="completion"?l`<div class="field-help">${n("schedule_preview_ontime",e)}</div>`:p;return l`
|
||||
</div>`}_toggleWeekday(e){this._weekdays=this._weekdays.includes(e)?this._weekdays.filter(t=>t!==e):[...this._weekdays,e]}_previewScheduleDict(){if(this._scheduleType==="one_time")return this._dueDate?{kind:"one_time",due_date:this._dueDate}:null;if(this._scheduleType==="calendar"&&!this._calendarEntity.trim())return null;if(bi.includes(this._scheduleType))return{...this._buildSchedule(),...this._recurrenceExtras()};let e=parseInt(this._intervalDays,10);return this._scheduleType==="manual"||!e||e<=0?null:{kind:"interval",every:e,unit:this._intervalUnit,anchor:this._intervalAnchor,...this._recurrenceExtras()}}updated(e){super.updated?.(e),this._scheduleEntityPickerProbe();for(let t of e.keys())if(g._PREVIEW_RELEVANT.has(String(t))){this._schedulePreviewRefresh();return}}_scheduleEntityPickerProbe(){this._entityPickerFallback||this._pickerProbeTimer!==void 0||!this._open||this._scheduleType!=="sensor_based"||(this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),1500))}_probeEntityPickers(){if(this._pickerProbeTimer=void 0,this._entityPickerFallback||!this._open)return;let e=this.shadowRoot?.querySelector("ha-form.entity-picker-form"),t=(this.shadowRoot?.querySelector(".content")?.offsetHeight??0)>0;if(!e||!t){this._pickerProbeStrikes=0;return}let i=(h,_,m=0)=>{if(!(!h||m>10)){(h.tagName?.toLowerCase()??"")==="ha-entity-picker"&&_.push(h);for(let b of[h.shadowRoot,h])if(b)for(let k of Array.from(b.children??[]))i(k,_,m+1)}},a=[...this.shadowRoot?.querySelectorAll("ha-form.entity-picker-form")??[]],o=[];for(let h of a)i(h,o);let d=o.length===0||o.some(h=>h.offsetHeight===0);if(e.offsetHeight===0||d){if(this._pickerProbeStrikes+=1,this._pickerProbeStrikes>=2){this._entityPickerFallback=!0;return}this._pickerProbeTimer=setTimeout(()=>this._probeEntityPickers(),700)}else this._pickerProbeStrikes=0}_schedulePreviewRefresh(){this._previewTimer&&clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._fetchSchedulePreview()},300)}async _fetchSchedulePreview(){let e=this._open?this._previewScheduleDict():null;if(!e){this._schedulePreview=[],this._schedulePreviewEnded=!1;return}let t=++this._previewSeq;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/schedule/preview",schedule:e,...this._lastPerformed?{last_performed:this._lastPerformed}:{}});if(t!==this._previewSeq)return;this._schedulePreview=i.occurrences||[],this._schedulePreviewEnded=!!i.series_ended}catch{}}_renderSchedulePreview(){if(this._schedulePreview.length===0)return p;let e=this._lang,t=this.scheduleTimeEnabled&&this._scheduleTimeOn&&this._scheduleTime?` ${this._scheduleTime}`:"",i=this._schedulePreview.map((o,d)=>{let h=new Date(`${o}T12:00:00`).getDay();return`${Ct(h===0?6:h-1,e,"short")} ${j(o,e)}${d===0?t:""}`}).join(" \xB7 "),a=this._scheduleType==="time_based"&&this._intervalAnchor==="completion"?l`<div class="field-help">${n("schedule_preview_ontime",e)}</div>`:p;return l`
|
||||
<div class="trigger-live-hint schedule-preview">
|
||||
${n("schedule_preview_title",e)}: ${i}${this._schedulePreviewEnded?l` <span class="field-help">${n("schedule_preview_ends",e)}</span>`:p}
|
||||
${a}
|
||||
</div>
|
||||
`}_buildSchedule(){let e=i=>{let a=parseInt(this._calOffset,10)||0;return a&&(i.offset=Math.max(-It,Math.min(a,It))),i};if(this._scheduleType==="weekdays")return e({kind:"weekdays",weekdays:[...this._weekdays].sort((i,a)=>i-a)});if(this._scheduleType==="nth_weekday")return e({kind:"nth_weekday",nth:parseInt(this._nth,10),weekday:parseInt(this._nthWeekday,10)});if(this._scheduleType==="calendar")return e({kind:"calendar",entity_id:this._calendarEntity.trim()});let t={kind:"day_of_month",day:this._domLastDay?-1:parseInt(this._domDay,10)||1};return this._domBusiness&&(t.business=!0),e(t)}_recurrenceExtras(){let e={};if(this._seasonMonths.length&&(e.season_months=[...this._seasonMonths].sort((t,i)=>t-i)),this._endsMode==="count"){let t=parseInt(this._endsCount,10);t>=1&&(e.ends={count:t})}else this._endsMode==="until"&&this._endsUntil&&(e.ends={until:this._endsUntil});return e}_toggleSeasonMonth(e){this._seasonMonths=this._seasonMonths.includes(e)?this._seasonMonths.filter(t=>t!==e):[...this._seasonMonths,e]}_renderRecurrenceExtras(){let e=this._lang;if(!(this._scheduleType==="time_based"||fi.includes(this._scheduleType)))return p;let i=Go(e);return l`
|
||||
`}_buildSchedule(){let e=i=>{let a=parseInt(this._calOffset,10)||0;return a&&(i.offset=Math.max(-It,Math.min(a,It))),i};if(this._scheduleType==="weekdays")return e({kind:"weekdays",weekdays:[...this._weekdays].sort((i,a)=>i-a)});if(this._scheduleType==="nth_weekday")return e({kind:"nth_weekday",nth:parseInt(this._nth,10),weekday:parseInt(this._nthWeekday,10)});if(this._scheduleType==="calendar")return e({kind:"calendar",entity_id:this._calendarEntity.trim()});let t={kind:"day_of_month",day:this._domLastDay?-1:parseInt(this._domDay,10)||1};return this._domBusiness&&(t.business=!0),e(t)}_recurrenceExtras(){let e={};if(this._seasonMonths.length&&(e.season_months=[...this._seasonMonths].sort((t,i)=>t-i)),this._endsMode==="count"){let t=parseInt(this._endsCount,10);t>=1&&(e.ends={count:t})}else this._endsMode==="until"&&this._endsUntil&&(e.ends={until:this._endsUntil});return e}_toggleSeasonMonth(e){this._seasonMonths=this._seasonMonths.includes(e)?this._seasonMonths.filter(t=>t!==e):[...this._seasonMonths,e]}_renderRecurrenceExtras(){let e=this._lang;if(!(this._scheduleType==="time_based"||bi.includes(this._scheduleType)))return p;let i=Zo(e);return l`
|
||||
<label class="field-label">${n("season_window_label",e)}</label>
|
||||
<div class="field-help">${n("season_window_hint",e)}</div>
|
||||
<div class="weekday-chips season-chips">
|
||||
@@ -2703,7 +2742,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._endsUntil}
|
||||
@value-changed=${a=>this._endsUntil=a.detail.value}
|
||||
></ms-date-field>`:p}
|
||||
`}_renderCalendarFields(){let e=this._lang,t=Vo(e);if(this._scheduleType==="weekdays")return l`
|
||||
`}_renderCalendarFields(){let e=this._lang,t=Qo(e);if(this._scheduleType==="weekdays")return l`
|
||||
<label class="field-label">${n("recurrence_on_days",e)}</label>
|
||||
<div class="weekday-chips">
|
||||
${t.map((i,a)=>l`
|
||||
@@ -2773,7 +2812,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
max=${It}
|
||||
.value=${this._calOffset}
|
||||
@input=${t=>this._calOffset=t.target.value}
|
||||
></ms-textfield>`}_thresholdLimitsOverlap(){let e=parseFloat(this._triggerAbove),t=parseFloat(this._triggerBelow);return!isNaN(e)&&!isNaN(t)&&t>e}_renderTriggerLiveHint(){if(this._triggerType==="compound")return p;let e=this._triggerType==="threshold"&&this._thresholdLimitsOverlap()?l`<div class="trigger-live-hint warn">${n("trigger_hint_overlap",this._lang)}</div>`:p,t=this._triggerEntityId||this._triggerEntityIds[0];if(!t||!this.hass?.states)return e;let i=this.hass.states[t];if(!i)return e;let a=this._lang,o=i.attributes?.unit_of_measurement,d=typeof o=="string"&&o?` ${o}`:"",h=this._triggerAttribute?i.attributes?.[this._triggerAttribute]:i.state,_=typeof h=="number"?h:parseFloat(String(h)),m=h!=="unknown"&&h!=="unavailable"&&h!=null&&!isNaN(_),b=w=>I(w,a,{maximumFractionDigits:1}),k=[];if(this._triggerType==="threshold"){let w=parseFloat(this._triggerAbove),v=parseFloat(this._triggerBelow);if(isNaN(w)&&isNaN(v))return p;m&&k.push(n("trigger_hint_now",a).replace("{value}",b(_)+d)),isNaN(w)||k.push(n("trigger_hint_above",a).replace("{target}",b(w)+d)),isNaN(v)||k.push(n("trigger_hint_below",a).replace("{target}",b(v)+d))}else if(this._triggerType==="counter"){let w=parseFloat(this._triggerTargetValue);if(isNaN(w))return p;this._triggerDeltaMode?this._taskId?k.push(n("trigger_hint_counter_delta_edit",a).replace("{target}",b(w)+d)):m?k.push(n("trigger_hint_counter_delta",a).replace("{value}",b(_)+d).replace("{due}",b(_+w)+d).replace("{target}",b(w)+d)):k.push(n("trigger_hint_counter_delta_edit",a).replace("{target}",b(w)+d)):(m&&k.push(n("trigger_hint_now",a).replace("{value}",b(_)+d)),k.push(n("trigger_hint_counter_abs",a).replace("{target}",b(w)+d)))}else if(this._triggerType==="runtime"){let w=parseFloat(this._triggerRuntimeHours);if(isNaN(w))return p;k.push(n("trigger_hint_runtime",a).replace("{hours}",b(w))),k.push(n("trigger_hint_state_now",a).replace("{value}",String(i.state)))}else if(this._triggerType==="state_change"){let w=parseInt(this._triggerTargetChanges,10)||1,v=this._triggerToState.trim();k.push((v?n("trigger_hint_state_change_to",a).replace("{state}",v):n("trigger_hint_state_change",a)).replace("{count}",String(w))),k.push(n("trigger_hint_state_now",a).replace("{value}",String(i.state)))}else if(this._triggerType==="due_date"){let w=parseInt(this._triggerDaysBefore,10)||0;k.push(w===0?n("trigger_hint_due_date_same_day",a):w===1?n("trigger_hint_due_date_one",a):n("trigger_hint_due_date",a).replace("{days}",String(w)));let v=h==null?"":String(h);v&&!isNaN(Date.parse(v))&&k.push(n("trigger_hint_due_date_now",a).replace("{date}",H(v,a)))}return k.length?l`<div class="trigger-live-hint">${k.join(" ")}</div>${e}`:e}_renderTriggerTypeFields(){let e=this._lang;return this._triggerType==="threshold"?l`
|
||||
></ms-textfield>`}_thresholdLimitsOverlap(){let e=parseFloat(this._triggerAbove),t=parseFloat(this._triggerBelow);return!isNaN(e)&&!isNaN(t)&&t>e}_renderTriggerLiveHint(){if(this._triggerType==="compound")return p;let e=this._triggerType==="threshold"&&this._thresholdLimitsOverlap()?l`<div class="trigger-live-hint warn">${n("trigger_hint_overlap",this._lang)}</div>`:p,t=this._triggerEntityId||this._triggerEntityIds[0];if(!t||!this.hass?.states)return e;let i=this.hass.states[t];if(!i)return e;let a=this._lang,o=i.attributes?.unit_of_measurement,d=typeof o=="string"&&o?` ${o}`:"",h=this._triggerAttribute?i.attributes?.[this._triggerAttribute]:i.state,_=typeof h=="number"?h:parseFloat(String(h)),m=h!=="unknown"&&h!=="unavailable"&&h!=null&&!isNaN(_),b=w=>I(w,a,{maximumFractionDigits:1}),k=[];if(this._triggerType==="threshold"){let w=parseFloat(this._triggerAbove),v=parseFloat(this._triggerBelow);if(isNaN(w)&&isNaN(v))return p;m&&k.push(n("trigger_hint_now",a).replace("{value}",b(_)+d)),isNaN(w)||k.push(n("trigger_hint_above",a).replace("{target}",b(w)+d)),isNaN(v)||k.push(n("trigger_hint_below",a).replace("{target}",b(v)+d))}else if(this._triggerType==="counter"){let w=parseFloat(this._triggerTargetValue);if(isNaN(w))return p;this._triggerDeltaMode?this._taskId?k.push(n("trigger_hint_counter_delta_edit",a).replace("{target}",b(w)+d)):m?k.push(n("trigger_hint_counter_delta",a).replace("{value}",b(_)+d).replace("{due}",b(_+w)+d).replace("{target}",b(w)+d)):k.push(n("trigger_hint_counter_delta_edit",a).replace("{target}",b(w)+d)):(m&&k.push(n("trigger_hint_now",a).replace("{value}",b(_)+d)),k.push(n("trigger_hint_counter_abs",a).replace("{target}",b(w)+d)))}else if(this._triggerType==="runtime"){let w=parseFloat(this._triggerRuntimeHours);if(isNaN(w))return p;k.push(n("trigger_hint_runtime",a).replace("{hours}",b(w))),k.push(n("trigger_hint_state_now",a).replace("{value}",String(i.state)))}else if(this._triggerType==="state_change"){let w=parseInt(this._triggerTargetChanges,10)||1,v=this._triggerToState.trim();k.push((v?n("trigger_hint_state_change_to",a).replace("{state}",v):n("trigger_hint_state_change",a)).replace("{count}",String(w))),k.push(n("trigger_hint_state_now",a).replace("{value}",String(i.state)))}else if(this._triggerType==="due_date"){let w=parseInt(this._triggerDaysBefore,10)||0;k.push(w===0?n("trigger_hint_due_date_same_day",a):w===1?n("trigger_hint_due_date_one",a):n("trigger_hint_due_date",a).replace("{days}",String(w)));let v=h==null?"":String(h);v&&!isNaN(Date.parse(v))&&k.push(n("trigger_hint_due_date_now",a).replace("{date}",j(v,a)))}return k.length?l`<div class="trigger-live-hint">${k.join(" ")}</div>${e}`:e}_renderTriggerTypeFields(){let e=this._lang;return this._triggerType==="threshold"?l`
|
||||
<ms-textfield
|
||||
label="${n("trigger_above",e)}"
|
||||
type="number"
|
||||
@@ -2846,7 +2885,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ms-textfield
|
||||
label="${n("target_changes",e)}"
|
||||
type="number"
|
||||
min=${tn[0]}
|
||||
min=${sn[0]}
|
||||
.value=${this._triggerTargetChanges}
|
||||
@input=${t=>this._triggerTargetChanges=t.target.value}
|
||||
></ms-textfield>
|
||||
@@ -2855,7 +2894,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ms-textfield
|
||||
label="${n("for_at_least_minutes",e)}"
|
||||
type="number"
|
||||
min=${en[0]}
|
||||
min=${tn[0]}
|
||||
.value=${this._triggerForMinutes}
|
||||
@input=${t=>this._triggerForMinutes=t.target.value}
|
||||
></ms-textfield>
|
||||
@@ -2918,7 +2957,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._type}
|
||||
@change=${i=>this._type=i.target.value}
|
||||
>
|
||||
${Do.map(i=>l`<option value=${i} ?selected=${i===this._type}>${n(i,e)}</option>`)}
|
||||
${Uo.map(i=>l`<option value=${i} ?selected=${i===this._type}>${n(i,e)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
${this._type==="reading"?l`
|
||||
@@ -2953,7 +2992,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._priority}
|
||||
@change=${i=>this._priority=i.target.value}
|
||||
>
|
||||
${Mo.map(i=>l`<option value=${i} ?selected=${i===this._priority}>${n("priority_"+i,e)}</option>`)}
|
||||
${Bo.map(i=>l`<option value=${i} ?selected=${i===this._priority}>${n("priority_"+i,e)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
@@ -2991,7 +3030,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.value=${this._scheduleType}
|
||||
@change=${i=>this._scheduleType=i.target.value}
|
||||
>
|
||||
${zo.map(i=>l`<option value=${i} ?selected=${i===this._scheduleType}>${n(i,e)}</option>`)}
|
||||
${Wo.map(i=>l`<option value=${i} ?selected=${i===this._scheduleType}>${n(i,e)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
${this._scheduleType==="time_based"?l`
|
||||
@@ -3024,7 +3063,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
@value-changed=${i=>this._dueDate=i.detail.value}
|
||||
></ms-date-field>
|
||||
`:p}
|
||||
${this.scheduleTimeEnabled&&Cn.includes(this._scheduleType)?l`
|
||||
${this.scheduleTimeEnabled&&Pn.includes(this._scheduleType)?l`
|
||||
<label class="checkbox-row schedule-time-toggle">
|
||||
<input type="checkbox" .checked=${this._scheduleTimeOn}
|
||||
@change=${i=>this._scheduleTimeOn=i.target.checked} />
|
||||
@@ -3046,8 +3085,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ms-textfield
|
||||
label="${n("warning_days",e)}"
|
||||
type="number"
|
||||
min=${os[0]}
|
||||
max=${os[1]}
|
||||
min=${ls[0]}
|
||||
max=${ls[1]}
|
||||
.value=${this._warningDays}
|
||||
@input=${i=>this._warningDays=i.target.value}
|
||||
></ms-textfield>
|
||||
@@ -3073,7 +3112,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
${this._renderPhasesEditor(e)}
|
||||
<h3>${n("require_on_completion",e)}</h3>
|
||||
<div class="required-completion">
|
||||
${cs.map(i=>l`
|
||||
${ds.map(i=>l`
|
||||
<label class="req-option">
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -3142,7 +3181,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<ha-form
|
||||
class="entity-picker-form"
|
||||
.hass=${this.hass}
|
||||
.schema=${[{name:"environmental_entity",selector:{entity:{domain:En,device_class:Sn}}}]}
|
||||
.schema=${[{name:"environmental_entity",selector:{entity:{domain:Sn,device_class:Tn}}}]}
|
||||
.data=${{environmental_entity:this._environmentalEntity}}
|
||||
.computeLabel=${()=>n("environmental_entity_optional",e)}
|
||||
.computeHelper=${()=>n("environmental_entity_helper",e)}
|
||||
@@ -3178,8 +3217,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
@value-changed=${i=>this._notifyIcon=i.detail.value||""}
|
||||
></ha-icon-picker>
|
||||
<div class="field-help notify-icon-help">
|
||||
<ha-icon icon=${this._notifyIcon.trim()||ys(this._type)}></ha-icon>
|
||||
${n("notify_icon_hint",e).replace("{default}",ys(this._type))}
|
||||
<ha-icon icon=${this._notifyIcon.trim()||xs(this._type)}></ha-icon>
|
||||
${n("notify_icon_hint",e).replace("{default}",xs(this._type))}
|
||||
</div>
|
||||
${this._availableTags.length>0?l`
|
||||
<div class="select-row">
|
||||
@@ -3657,11 +3696,11 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
`,c([f({attribute:!1})],g.prototype,"hass",2),c([f({type:Boolean,attribute:"checklists-enabled"})],g.prototype,"checklistsEnabled",2),c([f({type:Boolean,attribute:"schedule-time-enabled"})],g.prototype,"scheduleTimeEnabled",2),c([f({type:Boolean,attribute:"completion-actions-enabled"})],g.prototype,"completionActionsEnabled",2),c([f({type:Boolean,attribute:"adaptive-feature"})],g.prototype,"adaptiveFeature",2),c([f({type:Boolean,attribute:"seasonal-feature"})],g.prototype,"seasonalFeature",2),c([f({type:Boolean,attribute:"environmental-feature"})],g.prototype,"environmentalFeature",2),c([f({type:Number,attribute:"default-warning-days"})],g.prototype,"defaultWarningDays",2),c([u()],g.prototype,"parts",2),c([u()],g.prototype,"_foreignOwners",2),c([u()],g.prototype,"_open",2),c([u()],g.prototype,"_entityPickerFallback",2),c([u()],g.prototype,"_loading",2),c([u()],g.prototype,"_error",2),c([u()],g.prototype,"_warning",2),c([u()],g.prototype,"_entryId",2),c([u()],g.prototype,"_taskId",2),c([u()],g.prototype,"_isFleetTask",2),c([u()],g.prototype,"_objectChoices",2),c([u()],g.prototype,"_name",2),c([u()],g.prototype,"_type",2),c([u()],g.prototype,"_scheduleType",2),c([u()],g.prototype,"_intervalDays",2),c([u()],g.prototype,"_intervalUnit",2),c([u()],g.prototype,"_dueDate",2),c([u()],g.prototype,"_warningDays",2),c([u()],g.prototype,"_earliestCompletionDays",2),c([u()],g.prototype,"_intervalAnchor",2),c([u()],g.prototype,"_weekdays",2),c([u()],g.prototype,"_nth",2),c([u()],g.prototype,"_nthWeekday",2),c([u()],g.prototype,"_domDay",2),c([u()],g.prototype,"_domLastDay",2),c([u()],g.prototype,"_domBusiness",2),c([u()],g.prototype,"_calOffset",2),c([u()],g.prototype,"_calendarEntity",2),c([u()],g.prototype,"_seasonMonths",2),c([u()],g.prototype,"_endsMode",2),c([u()],g.prototype,"_endsCount",2),c([u()],g.prototype,"_endsUntil",2),c([u()],g.prototype,"_schedulePreview",2),c([u()],g.prototype,"_schedulePreviewEnded",2),c([u()],g.prototype,"_notes",2),c([u()],g.prototype,"_documentationUrl",2),c([u()],g.prototype,"_customIcon",2),c([u()],g.prototype,"_notifyIcon",2),c([u()],g.prototype,"_priority",2),c([u()],g.prototype,"_labels",2),c([u()],g.prototype,"_mirrorTodoEntities",2),c([u()],g.prototype,"_enabled",2),c([u()],g.prototype,"_triggerEntityId",2),c([u()],g.prototype,"_triggerEntityIds",2),c([u()],g.prototype,"_triggerEntityLogic",2),c([u()],g.prototype,"_triggerAttribute",2),c([u()],g.prototype,"_triggerType",2),c([u()],g.prototype,"_triggerAbove",2),c([u()],g.prototype,"_triggerBelow",2),c([u()],g.prototype,"_triggerEquals",2),c([u()],g.prototype,"_triggerNotEquals",2),c([u()],g.prototype,"_triggerForMinutes",2),c([u()],g.prototype,"_triggerCombinator",2),c([u()],g.prototype,"_triggerTargetValue",2),c([u()],g.prototype,"_triggerDeltaMode",2),c([u()],g.prototype,"_triggerBaselineValue",2),c([u()],g.prototype,"_liveBaselineValue",2),c([u()],g.prototype,"_autoCompleteOnRecovery",2),c([u()],g.prototype,"_triggerFromState",2),c([u()],g.prototype,"_triggerToState",2),c([u()],g.prototype,"_triggerTargetChanges",2),c([u()],g.prototype,"_triggerRuntimeHours",2),c([u()],g.prototype,"_triggerDaysBefore",2),c([u()],g.prototype,"_triggerRuntimeMaxSession",2),c([u()],g.prototype,"_triggerOnStates",2),c([u()],g.prototype,"_compoundLogic",2),c([u()],g.prototype,"_compoundConditions",2),c([u()],g.prototype,"_suggestedAttributes",2),c([u()],g.prototype,"_availableAttributes",2),c([u()],g.prototype,"_entityDomain",2),c([u()],g.prototype,"_lastPerformed",2),c([u()],g.prototype,"_nfcTagId",2),c([u()],g.prototype,"_requireTagScan",2),c([u()],g.prototype,"_allowSkip",2),c([u()],g.prototype,"_notifyEnabled",2),c([u()],g.prototype,"_readingUnit",2),c([u()],g.prototype,"_readings",2),c([u()],g.prototype,"_consumesParts",2),c([u()],g.prototype,"_consumesQtyText",2),c([u()],g.prototype,"_partsLoadFailed",2),c([u()],g.prototype,"_availableTags",2),c([u()],g.prototype,"_responsibleUserId",2),c([u()],g.prototype,"_assigneePool",2),c([u()],g.prototype,"_rotationStrategy",2),c([u()],g.prototype,"_availableUsers",2),c([u()],g.prototype,"_checklistText",2),c([u()],g.prototype,"_phaseDefs",2),c([u()],g.prototype,"_phaseSeq",2),c([u()],g.prototype,"_requiredCompletion",2),c([u()],g.prototype,"_scheduleTime",2),c([u()],g.prototype,"_scheduleTimeOn",2),c([u()],g.prototype,"_actionService",2),c([u()],g.prototype,"_actionTargetEntity",2),c([u()],g.prototype,"_actionSkipAuto",2),c([u()],g.prototype,"_actionPresent",2),c([u()],g.prototype,"_actionData",2),c([u()],g.prototype,"_actionDataJsonFallback",2),c([u()],g.prototype,"_actionTesting",2),c([u()],g.prototype,"_actionTestResult",2),c([u()],g.prototype,"_actionTestError",2),c([u()],g.prototype,"_qcNotes",2),c([u()],g.prototype,"_qcCost",2),c([u()],g.prototype,"_qcDuration",2),c([u()],g.prototype,"_qcFeedback",2),c([u()],g.prototype,"_environmentalEntity",2),c([u()],g.prototype,"_environmentalAttribute",2),c([u()],g.prototype,"_adaptiveEnabled",2),c([u()],g.prototype,"_adaptiveAlpha",2),c([u()],g.prototy
Line truncated
|
||||
`,c([f({attribute:!1})],g.prototype,"hass",2),c([f({type:Boolean,attribute:"checklists-enabled"})],g.prototype,"checklistsEnabled",2),c([f({type:Boolean,attribute:"schedule-time-enabled"})],g.prototype,"scheduleTimeEnabled",2),c([f({type:Boolean,attribute:"completion-actions-enabled"})],g.prototype,"completionActionsEnabled",2),c([f({type:Boolean,attribute:"adaptive-feature"})],g.prototype,"adaptiveFeature",2),c([f({type:Boolean,attribute:"seasonal-feature"})],g.prototype,"seasonalFeature",2),c([f({type:Boolean,attribute:"environmental-feature"})],g.prototype,"environmentalFeature",2),c([f({type:Number,attribute:"default-warning-days"})],g.prototype,"defaultWarningDays",2),c([u()],g.prototype,"parts",2),c([u()],g.prototype,"_foreignOwners",2),c([u()],g.prototype,"_open",2),c([u()],g.prototype,"_entityPickerFallback",2),c([u()],g.prototype,"_loading",2),c([u()],g.prototype,"_error",2),c([u()],g.prototype,"_warning",2),c([u()],g.prototype,"_entryId",2),c([u()],g.prototype,"_taskId",2),c([u()],g.prototype,"_isFleetTask",2),c([u()],g.prototype,"_objectChoices",2),c([u()],g.prototype,"_name",2),c([u()],g.prototype,"_type",2),c([u()],g.prototype,"_scheduleType",2),c([u()],g.prototype,"_intervalDays",2),c([u()],g.prototype,"_intervalUnit",2),c([u()],g.prototype,"_dueDate",2),c([u()],g.prototype,"_warningDays",2),c([u()],g.prototype,"_earliestCompletionDays",2),c([u()],g.prototype,"_intervalAnchor",2),c([u()],g.prototype,"_weekdays",2),c([u()],g.prototype,"_nth",2),c([u()],g.prototype,"_nthWeekday",2),c([u()],g.prototype,"_domDay",2),c([u()],g.prototype,"_domLastDay",2),c([u()],g.prototype,"_domBusiness",2),c([u()],g.prototype,"_calOffset",2),c([u()],g.prototype,"_calendarEntity",2),c([u()],g.prototype,"_seasonMonths",2),c([u()],g.prototype,"_endsMode",2),c([u()],g.prototype,"_endsCount",2),c([u()],g.prototype,"_endsUntil",2),c([u()],g.prototype,"_schedulePreview",2),c([u()],g.prototype,"_schedulePreviewEnded",2),c([u()],g.prototype,"_notes",2),c([u()],g.prototype,"_documentationUrl",2),c([u()],g.prototype,"_customIcon",2),c([u()],g.prototype,"_notifyIcon",2),c([u()],g.prototype,"_priority",2),c([u()],g.prototype,"_labels",2),c([u()],g.prototype,"_mirrorTodoEntities",2),c([u()],g.prototype,"_enabled",2),c([u()],g.prototype,"_triggerEntityId",2),c([u()],g.prototype,"_triggerEntityIds",2),c([u()],g.prototype,"_triggerEntityLogic",2),c([u()],g.prototype,"_triggerAttribute",2),c([u()],g.prototype,"_triggerType",2),c([u()],g.prototype,"_triggerAbove",2),c([u()],g.prototype,"_triggerBelow",2),c([u()],g.prototype,"_triggerEquals",2),c([u()],g.prototype,"_triggerNotEquals",2),c([u()],g.prototype,"_triggerForMinutes",2),c([u()],g.prototype,"_triggerCombinator",2),c([u()],g.prototype,"_triggerTargetValue",2),c([u()],g.prototype,"_triggerDeltaMode",2),c([u()],g.prototype,"_triggerBaselineValue",2),c([u()],g.prototype,"_liveBaselineValue",2),c([u()],g.prototype,"_autoCompleteOnRecovery",2),c([u()],g.prototype,"_triggerFromState",2),c([u()],g.prototype,"_triggerToState",2),c([u()],g.prototype,"_triggerTargetChanges",2),c([u()],g.prototype,"_triggerRuntimeHours",2),c([u()],g.prototype,"_triggerDaysBefore",2),c([u()],g.prototype,"_triggerRuntimeMaxSession",2),c([u()],g.prototype,"_triggerOnStates",2),c([u()],g.prototype,"_compoundLogic",2),c([u()],g.prototype,"_compoundConditions",2),c([u()],g.prototype,"_suggestedAttributes",2),c([u()],g.prototype,"_availableAttributes",2),c([u()],g.prototype,"_entityDomain",2),c([u()],g.prototype,"_lastPerformed",2),c([u()],g.prototype,"_nfcTagId",2),c([u()],g.prototype,"_requireTagScan",2),c([u()],g.prototype,"_allowSkip",2),c([u()],g.prototype,"_notifyEnabled",2),c([u()],g.prototype,"_readingUnit",2),c([u()],g.prototype,"_readings",2),c([u()],g.prototype,"_consumesParts",2),c([u()],g.prototype,"_consumesQtyText",2),c([u()],g.prototype,"_partsLoadFailed",2),c([u()],g.prototype,"_availableTags",2),c([u()],g.prototype,"_responsibleUserId",2),c([u()],g.prototype,"_assigneePool",2),c([u()],g.prototype,"_rotationStrategy",2),c([u()],g.prototype,"_availableUsers",2),c([u()],g.prototype,"_checklistText",2),c([u()],g.prototype,"_phaseDefs",2),c([u()],g.prototype,"_phaseSeq",2),c([u()],g.prototype,"_requiredCompletion",2),c([u()],g.prototype,"_scheduleTime",2),c([u()],g.prototype,"_scheduleTimeOn",2),c([u()],g.prototype,"_actionService",2),c([u()],g.prototype,"_actionTargetEntity",2),c([u()],g.prototype,"_actionSkipAuto",2),c([u()],g.prototype,"_actionPresent",2),c([u()],g.prototype,"_actionData",2),c([u()],g.prototype,"_actionDataJsonFallback",2),c([u()],g.prototype,"_actionTesting",2),c([u()],g.prototype,"_actionTestResult",2),c([u()],g.prototype,"_actionTestError",2),c([u()],g.prototype,"_qcNotes",2),c([u()],g.prototype,"_qcCost",2),c([u()],g.prototype,"_qcDuration",2),c([u()],g.prototype,"_qcFeedback",2),c([u()],g.prototype,"_environmentalEntity",2),c([u()],g.prototype,"_environmentalAttribute",2),c([u()],g.prototype,"_adaptiveEnabled",2),c([u()],g.prototype,"_adaptiveAlpha",2),c([u()],g.prototy
Line truncated
|
||||
<div class="backdrop" @click=${r}></div>
|
||||
<div class="dialog" role="dialog" aria-modal="true" tabindex="-1"
|
||||
@keydown=${e=>Ko(e,r)}>${s}</div>
|
||||
`}function ct(r){r?.querySelector(".dialog")?.focus({preventScroll:!0})}var ot,vi=y(()=>{"use strict";R();ot=$`
|
||||
@keydown=${e=>Jo(e,r)}>${s}</div>
|
||||
`}function ct(r){r?.querySelector(".dialog")?.focus({preventScroll:!0})}var ot,yi=y(()=>{"use strict";R();ot=$`
|
||||
.backdrop {
|
||||
position: fixed; inset: 0; z-index: 100;
|
||||
background: rgba(0,0,0,0.5);
|
||||
@@ -3681,7 +3720,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
}
|
||||
/* Focused programmatically on open (so Escape works at once) — no ring. */
|
||||
.dialog:focus { outline: none; }
|
||||
`});var U,jn=y(()=>{"use strict";R();j();C();U=class extends E{constructor(){super(...arguments);this._open=!1;this._title="";this._message="";this._confirmText="";this._danger=!1;this._inputLabel="";this._inputType="";this._options=null;this._inputValue="";this._resolve=null;this._promptResolve=null}confirm(e){return this._title=e.title,this._message=e.message,this._confirmText=e.confirmText||"OK",this._danger=e.danger||!1,this._inputLabel="",this._inputType="",this._options=null,this._inputValue="",this._open=!0,new Promise(t=>{this._resolve=t,this._promptResolve=null})}prompt(e){return this._title=e.title,this._message=e.message,this._confirmText=e.confirmText||"OK",this._danger=e.danger||!1,this._inputLabel=e.inputLabel||"",this._inputType=e.inputType||"text",this._options=e.options&&e.options.length?e.options:null,this._inputValue=e.inputValue||"",this._open=!0,new Promise(t=>{this._promptResolve=t,this._resolve=null})}_cancel(){this._open=!1,this._promptResolve&&(this._promptResolve({confirmed:!1,value:""}),this._promptResolve=null),this._resolve?.(!1),this._resolve=null}_confirmAction(){this._open=!1,this._promptResolve&&(this._promptResolve({confirmed:!0,value:this._inputValue}),this._promptResolve=null),this._resolve?.(!0),this._resolve=null}render(){if(!this._open)return p;let e=P(this.hass);return l`
|
||||
`});var U,Dn=y(()=>{"use strict";R();H();C();U=class extends E{constructor(){super(...arguments);this._open=!1;this._title="";this._message="";this._confirmText="";this._danger=!1;this._inputLabel="";this._inputType="";this._options=null;this._inputValue="";this._resolve=null;this._promptResolve=null}confirm(e){return this._title=e.title,this._message=e.message,this._confirmText=e.confirmText||"OK",this._danger=e.danger||!1,this._inputLabel="",this._inputType="",this._options=null,this._inputValue="",this._open=!0,new Promise(t=>{this._resolve=t,this._promptResolve=null})}prompt(e){return this._title=e.title,this._message=e.message,this._confirmText=e.confirmText||"OK",this._danger=e.danger||!1,this._inputLabel=e.inputLabel||"",this._inputType=e.inputType||"text",this._options=e.options&&e.options.length?e.options:null,this._inputValue=e.inputValue||"",this._open=!0,new Promise(t=>{this._promptResolve=t,this._resolve=null})}_cancel(){this._open=!1,this._promptResolve&&(this._promptResolve({confirmed:!1,value:""}),this._promptResolve=null),this._resolve?.(!1),this._resolve=null}_confirmAction(){this._open=!1,this._promptResolve&&(this._promptResolve({confirmed:!0,value:this._inputValue}),this._promptResolve=null),this._resolve?.(!0),this._resolve=null}render(){if(!this._open)return p;let e=P(this.hass);return l`
|
||||
<ha-dialog open @closed=${this._cancel}>
|
||||
<div class="dialog-title">${this._title}</div>
|
||||
<div class="content">
|
||||
@@ -3745,7 +3784,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
ha-button.danger {
|
||||
--mdc-theme-primary: var(--error-color, #f44336);
|
||||
}
|
||||
`],c([f({attribute:!1})],U.prototype,"hass",2),c([u()],U.prototype,"_open",2),c([u()],U.prototype,"_title",2),c([u()],U.prototype,"_message",2),c([u()],U.prototype,"_confirmText",2),c([u()],U.prototype,"_danger",2),c([u()],U.prototype,"_inputLabel",2),c([u()],U.prototype,"_inputType",2),c([u()],U.prototype,"_options",2),c([u()],U.prototype,"_inputValue",2);customElements.get("maintenance-confirm-dialog")||customElements.define("maintenance-confirm-dialog",U)});function Yo(){return document.querySelector("home-assistant")?.shadowRoot??document.body}function He(r,s){let e=Yo(),t=e.querySelector(`${Hn}[${qn}]`);return t||(t=document.createElement(Hn),t.setAttribute(qn,""),e.appendChild(t)),t.hass=r,t.confirm(s)}var Hn,qn,bi=y(()=>{"use strict";jn();Hn="maintenance-confirm-dialog",qn="data-ms-lovelace-confirm"});var Se,ws=y(()=>{"use strict";R();j();Xi();Se=class extends E{constructor(){super(...arguments);this.docId="";this._url="";this._failed=!1;this._signedFor=""}updated(){this.hass&&this.docId&&this._signedFor!==this.docId&&(this._signedFor=this.docId,this._url="",this._failed=!1,this._sign())}async _sign(){try{this._url=await Ji(this.hass,this.docId)}catch{this._failed=!0}}render(){return this._failed||!this.docId?p:this._url?l`
|
||||
`],c([f({attribute:!1})],U.prototype,"hass",2),c([u()],U.prototype,"_open",2),c([u()],U.prototype,"_title",2),c([u()],U.prototype,"_message",2),c([u()],U.prototype,"_confirmText",2),c([u()],U.prototype,"_danger",2),c([u()],U.prototype,"_inputLabel",2),c([u()],U.prototype,"_inputType",2),c([u()],U.prototype,"_options",2),c([u()],U.prototype,"_inputValue",2);customElements.get("maintenance-confirm-dialog")||customElements.define("maintenance-confirm-dialog",U)});function Xo(){return document.querySelector("home-assistant")?.shadowRoot??document.body}function je(r,s){let e=Xo(),t=e.querySelector(`${Mn}[${zn}]`);return t||(t=document.createElement(Mn),t.setAttribute(zn,""),e.appendChild(t)),t.hass=r,t.confirm(s)}var Mn,zn,xi=y(()=>{"use strict";Dn();Mn="maintenance-confirm-dialog",zn="data-ms-lovelace-confirm"});var Se,ks=y(()=>{"use strict";R();H();es();Se=class extends E{constructor(){super(...arguments);this.docId="";this._url="";this._failed=!1;this._signedFor=""}updated(){this.hass&&this.docId&&this._signedFor!==this.docId&&(this._signedFor=this.docId,this._url="",this._failed=!1,this._sign())}async _sign(){try{this._url=await Xi(this.hass,this.docId)}catch{this._failed=!0}}render(){return this._failed||!this.docId?p:this._url?l`
|
||||
<a href=${this._url} target="_blank" rel="noopener" class="wrap">
|
||||
<img src=${this._url} alt="" loading="lazy"
|
||||
@error=${()=>this._failed=!0} />
|
||||
@@ -3769,7 +3808,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
background: var(--secondary-background-color, rgba(0,0,0,0.06));
|
||||
margin-top: 4px;
|
||||
}
|
||||
`,c([f({attribute:!1})],Se.prototype,"hass",2),c([f()],Se.prototype,"docId",2),c([u()],Se.prototype,"_url",2),c([u()],Se.prototype,"_failed",2);customElements.get("maintenance-history-photo")||customElements.define("maintenance-history-photo",Se)});var B,Dn=y(()=>{"use strict";R();j();C();ke();ee();Lt();vi();us();ui();bi();rt();hi();ws();Ht();Ht();B=class extends E{constructor(){super(...arguments);this._open=!1;this._saving=!1;this._error="";this._draft=null;this._originalSnapshot=null;this._partOptions=null;this._partQty={};this._partQtyOriginal="";this._openGen=0;this._photos=new tt(this,{entryId:()=>this._draft?.entry_id??"",hass:()=>this.hass});this._photosOriginal="";this._readingRows=[];this._readingText={};this._readingsOriginal=""}get _lang(){return P(this.hass)}openEdit(e){this._openGen++,this._draft={...e},this._originalSnapshot={...e},this._error="",this._open=!0,this._partOptions=null,this._partQty={},this._partQtyOriginal="",this._photos.reset(e.photo_doc_ids??[]),this._photosOriginal=JSON.stringify(this._photos.ids),this._seedReadings(e),this._loadPartOptions()}_seedReadings(e){let t=[],i=new Set;for(let o of e.readings??[])i.has(o.id)||(i.add(o.id),t.push({id:o.id,name:o.name,unit:o.unit??null}));for(let o of e.reading_values??[])i.has(o.id)||(i.add(o.id),t.push({id:o.id,name:o.name,unit:o.unit??null}));this._readingRows=t;let a={};for(let o of e.reading_values??[])a[o.id]=String(o.value);this._readingText=a,this._readingsOriginal=JSON.stringify(this._readingNumbers())}_readingNumbers(){let e={};for(let t of this._readingRows){let i=(this._readingText[t.id]??"").trim();if(i==="")continue;let a=parseFloat(i.replace(",","."));isNaN(a)||(e[t.id]=a)}return e}async _loadPartOptions(){let e=this._draft;if(!e)return;let t=this._openGen;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/parts/overview"});if(t!==this._openGen)return;let a=[];for(let d of i.parts||[]){let h=d.entry_id===e.entry_id,_=d.consumers.some(m=>m.entry_id===e.entry_id&&m.task_id===e.task_id);!h&&!_||a.push({part_id:d.part_id,name:d.name,entry_id:d.entry_id,foreign:!h,object_name:d.object_name})}for(let d of e.used_parts||[]){let h=d.entry_id||e.entry_id;a.some(_=>_.part_id===d.part_id&&_.entry_id===h)||a.push({part_id:d.part_id,name:d.name||d.part_id,entry_id:h,foreign:h!==e.entry_id,object_name:null})}let o={};for(let d of e.used_parts||[])o[`${d.entry_id||e.entry_id}:${d.part_id}`]=d.quantity??1;this._partOptions=a,this._partQty=o,this._partQtyOriginal=this._partSelectionKey()}catch{if(t!==this._openGen)return;this._partOptions=[]}}_partSelectionKey(){return JSON.stringify(Object.entries(this._partQty).filter(([,e])=>e>0).sort(([e],[t])=>e.localeCompare(t)))}close(){this._openGen++,this._open=!1,this._error="",this._draft=null,this._originalSnapshot=null,this._photos.discardOrphans()}_set(e,t){this._draft&&(this._draft={...this._draft,[e]:t})}async _delete(){if(!this._draft||!this._originalSnapshot)return;let e=this._lang,i=(this._originalSnapshot.used_parts||[]).filter(h=>h.quantity>0).map(h=>`${I(h.quantity,e)}\xD7 ${h.name||h.part_id}`).join(", ");if(!await He(this.hass,{title:n("history_delete_entry",e),message:i?`${n("history_delete_confirm_entry",e)} ${n("history_delete_returns_parts",e).replace("{parts}",i)}`:n("history_delete_confirm_entry",e),confirmText:n("delete",e),danger:!0})||!this._draft||!this._originalSnapshot)return;let o=this._draft;this._error="",await this._runWs({type:"maintenance_supporter/task/history/delete",entry_id:o.entry_id,task_id:o.task_id,timestamp:this._originalSnapshot.original_timestamp})!==void 0&&(this.dispatchEvent(new CustomEvent("history-entry-saved",{detail:{entry_id:o.entry_id,task_id:o.task_id,deleted:!0},bubbles:!0,composed:!0})),this.close())}_runWs(e){return q(this,e,{busy:t=>{this._saving=t},onError:t=>{this._error=t}})}async _save(){if(!this._draft||!this._originalSnapshot||this._saving||this._photos.uploading)return;this._error="",this._photos.clearError();let e=this._draft,t=this._originalSnapshot,i={type:"maintenance_supporter/task/history/update",entry_id:e.entry_id,task_id:e.task_id,original_timestamp:t.original_timestamp};if(e.timestamp!==t.timestamp&&(i.timestamp=e.timestamp),e.notes!==t.notes&&(i.notes=e.notes),e.cost!==t.cost&&(i.cost=e.cost),e.duration!==t.duration&&(i.duration=e.duration),e.completed_by!==t.completed_by&&(i.completed_by=e.completed_by),this._partOptions!==null&&this._partSelectionKey()!==this._partQtyOriginal&&(i.used_parts=(this._partOptions||[]).filter(o=>(this._partQty[`${o.entry_id}:${o.part_id}`]||0)>0).map(o=>({part_id:o.part_id,quantity:this._partQty[`${o.entry_id}:${o.part_id}`],...o.foreign?{entry_id:o.entry_id}:{}}))),e.reading_value!==t.reading_value&&(i.reading_value=e.reading_value??null),this._readingRows.length>0&&JSON.stringify(this._readingNumbers())!==this._readingsOriginal){let o=this._readingNumbers(),d={};for(let h of this._readingRows)d[h.id]=o[h.id]??null;i.reading_values=d}if(JSON.stringify
Line truncated
|
||||
`,c([f({attribute:!1})],Se.prototype,"hass",2),c([f()],Se.prototype,"docId",2),c([u()],Se.prototype,"_url",2),c([u()],Se.prototype,"_failed",2);customElements.get("maintenance-history-photo")||customElements.define("maintenance-history-photo",Se)});var W,Fn=y(()=>{"use strict";R();H();C();ke();ee();Lt();yi();hs();hi();xi();rt();_i();ks();jt();jt();W=class extends E{constructor(){super(...arguments);this._open=!1;this._saving=!1;this._error="";this._draft=null;this._originalSnapshot=null;this._partOptions=null;this._partQty={};this._partQtyOriginal="";this._openGen=0;this._photos=new tt(this,{entryId:()=>this._draft?.entry_id??"",hass:()=>this.hass});this._photosOriginal="";this._readingRows=[];this._readingText={};this._readingsOriginal=""}get _lang(){return P(this.hass)}openEdit(e){this._openGen++,this._draft={...e},this._originalSnapshot={...e},this._error="",this._open=!0,this._partOptions=null,this._partQty={},this._partQtyOriginal="",this._photos.reset(e.photo_doc_ids??[]),this._photosOriginal=JSON.stringify(this._photos.ids),this._seedReadings(e),this._loadPartOptions()}_seedReadings(e){let t=[],i=new Set;for(let o of e.readings??[])i.has(o.id)||(i.add(o.id),t.push({id:o.id,name:o.name,unit:o.unit??null}));for(let o of e.reading_values??[])i.has(o.id)||(i.add(o.id),t.push({id:o.id,name:o.name,unit:o.unit??null}));this._readingRows=t;let a={};for(let o of e.reading_values??[])a[o.id]=String(o.value);this._readingText=a,this._readingsOriginal=JSON.stringify(this._readingNumbers())}_readingNumbers(){let e={};for(let t of this._readingRows){let i=(this._readingText[t.id]??"").trim();if(i==="")continue;let a=parseFloat(i.replace(",","."));isNaN(a)||(e[t.id]=a)}return e}async _loadPartOptions(){let e=this._draft;if(!e)return;let t=this._openGen;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/parts/overview"});if(t!==this._openGen)return;let a=[];for(let d of i.parts||[]){let h=d.entry_id===e.entry_id,_=d.consumers.some(m=>m.entry_id===e.entry_id&&m.task_id===e.task_id);!h&&!_||a.push({part_id:d.part_id,name:d.name,entry_id:d.entry_id,foreign:!h,object_name:d.object_name})}for(let d of e.used_parts||[]){let h=d.entry_id||e.entry_id;a.some(_=>_.part_id===d.part_id&&_.entry_id===h)||a.push({part_id:d.part_id,name:d.name||d.part_id,entry_id:h,foreign:h!==e.entry_id,object_name:null})}let o={};for(let d of e.used_parts||[])o[`${d.entry_id||e.entry_id}:${d.part_id}`]=d.quantity??1;this._partOptions=a,this._partQty=o,this._partQtyOriginal=this._partSelectionKey()}catch{if(t!==this._openGen)return;this._partOptions=[]}}_partSelectionKey(){return JSON.stringify(Object.entries(this._partQty).filter(([,e])=>e>0).sort(([e],[t])=>e.localeCompare(t)))}close(){this._openGen++,this._open=!1,this._error="",this._draft=null,this._originalSnapshot=null,this._photos.discardOrphans()}_set(e,t){this._draft&&(this._draft={...this._draft,[e]:t})}async _delete(){if(!this._draft||!this._originalSnapshot)return;let e=this._lang,i=(this._originalSnapshot.used_parts||[]).filter(h=>h.quantity>0).map(h=>`${I(h.quantity,e)}\xD7 ${h.name||h.part_id}`).join(", ");if(!await je(this.hass,{title:n("history_delete_entry",e),message:i?`${n("history_delete_confirm_entry",e)} ${n("history_delete_returns_parts",e).replace("{parts}",i)}`:n("history_delete_confirm_entry",e),confirmText:n("delete",e),danger:!0})||!this._draft||!this._originalSnapshot)return;let o=this._draft;this._error="",await this._runWs({type:"maintenance_supporter/task/history/delete",entry_id:o.entry_id,task_id:o.task_id,timestamp:this._originalSnapshot.original_timestamp})!==void 0&&(this.dispatchEvent(new CustomEvent("history-entry-saved",{detail:{entry_id:o.entry_id,task_id:o.task_id,deleted:!0},bubbles:!0,composed:!0})),this.close())}_runWs(e){return q(this,e,{busy:t=>{this._saving=t},onError:t=>{this._error=t}})}async _save(){if(!this._draft||!this._originalSnapshot||this._saving||this._photos.uploading)return;this._error="",this._photos.clearError();let e=this._draft,t=this._originalSnapshot,i={type:"maintenance_supporter/task/history/update",entry_id:e.entry_id,task_id:e.task_id,original_timestamp:t.original_timestamp};if(e.timestamp!==t.timestamp&&(i.timestamp=e.timestamp),e.notes!==t.notes&&(i.notes=e.notes),e.cost!==t.cost&&(i.cost=e.cost),e.duration!==t.duration&&(i.duration=e.duration),e.completed_by!==t.completed_by&&(i.completed_by=e.completed_by),this._partOptions!==null&&this._partSelectionKey()!==this._partQtyOriginal&&(i.used_parts=(this._partOptions||[]).filter(o=>(this._partQty[`${o.entry_id}:${o.part_id}`]||0)>0).map(o=>({part_id:o.part_id,quantity:this._partQty[`${o.entry_id}:${o.part_id}`],...o.foreign?{entry_id:o.entry_id}:{}}))),e.reading_value!==t.reading_value&&(i.reading_value=e.reading_value??null),this._readingRows.length>0&&JSON.stringify(this._readingNumbers())!==this._readingsOriginal){let o=this._readingNumbers(),d={};for(let h of this._readingRows)d[h.id]=o[h.id]??null;i.reading_values=d}if(JSON.stringify
Line truncated
|
||||
<h2>${n("history_edit_title",e)}</h2>
|
||||
<div class="entry-type">
|
||||
<ha-icon icon="mdi:tag-outline"></ha-icon>
|
||||
@@ -3781,8 +3820,8 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
.hass=${this.hass}
|
||||
.lang=${e}
|
||||
.label=${n("history_edit_timestamp",e)}
|
||||
.helper=${Xt(e)}
|
||||
.value=${Tr(t.timestamp)}
|
||||
.helper=${ei(e)}
|
||||
.value=${Ar(t.timestamp)}
|
||||
@value-changed=${a=>{let o=a.detail.value;o&&this._set("timestamp",o)}}
|
||||
></ms-date-field>
|
||||
<label>
|
||||
@@ -3869,7 +3908,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
<input type="number" step="any"
|
||||
.value=${e.reading_value!=null?String(e.reading_value):""}
|
||||
@input=${i=>{let a=i.target.value,o=a===""?NaN:Number(a);this._set("reading_value",isNaN(o)?null:o)}} />
|
||||
</label>`}};B.styles=[mi,ot,$`
|
||||
</label>`}};W.styles=[gi,ot,$`
|
||||
:host { display: contents; --ms-modal-max-height: 90vh; --ms-modal-gap: 12px; }
|
||||
h2 { margin: 0; font-size: 18px; }
|
||||
.entry-type {
|
||||
@@ -3959,7 +3998,7 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
}
|
||||
/* the camera / gallery pickers come from photoPickerStyles (ms-photo-picker) */
|
||||
.photos-hint { font-size: 12px; color: var(--secondary-text-color); }
|
||||
`],c([f({attribute:!1})],B.prototype,"hass",2),c([u()],B.prototype,"_open",2),c([u()],B.prototype,"_saving",2),c([u()],B.prototype,"_error",2),c([u()],B.prototype,"_draft",2),c([u()],B.prototype,"_partOptions",2),c([u()],B.prototype,"_partQty",2),c([u()],B.prototype,"_readingRows",2),c([u()],B.prototype,"_readingText",2);customElements.get("maintenance-history-edit-dialog")||customElements.define("maintenance-history-edit-dialog",B)});function qe(r){return r.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function ks(r){return!r.startsWith("data:image/svg+xml,")&&!r.startsWith("data:image/png;base64,")?"":qe(r)}function Qo(r){return r.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var W,Mn=y(()=>{"use strict";R();j();C();Zi();W=class extends E{constructor(){super(...arguments);this.lang="";this._open=!1;this._loading=!1;this._error="";this._viewResult=null;this._completeResult=null;this._quickResult=null;this._urlMode="companion";this._entryId="";this._taskId=null;this._objectName="";this._taskName="";this._generateSeq=0}get _dialogLang(){return this.lang||P(this.hass)}openForObject(e,t){this._entryId=e,this._taskId=null,this._objectName=t,this._taskName="",this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null,this._open=!0,this._generate()}openForTask(e,t,i,a){this._entryId=e,this._taskId=t,this._objectName=i,this._taskName=a,this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null,this._open=!0,this._generate()}async _generate(){let e=++this._generateSeq;this._loading=!0,this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null;try{let t={type:"maintenance_supporter/qr/generate",entry_id:this._entryId,url_mode:this._urlMode};this._taskId&&(t.task_id=this._taskId);let i=[this.hass.connection.sendMessagePromise({...t,action:"view"})];this._taskId&&(i.push(this.hass.connection.sendMessagePromise({...t,action:"complete"})),await this._hasQuickCompleteDefaults()&&i.push(this.hass.connection.sendMessagePromise({...t,action:"quick_complete"})));let a=await Promise.all(i);if(e!==this._generateSeq)return;this._viewResult=a[0],a.length>1&&(this._completeResult=a[1]),a.length>2&&(this._quickResult=a[2])}catch(t){if(e!==this._generateSeq)return;let i=t?.code,a=t?.message;this._error=i==="no_url"||typeof a=="string"&&a.includes("No Home Assistant URL")?n("qr_error_no_url",this._dialogLang):n("qr_error",this._dialogLang)}finally{e===this._generateSeq&&(this._loading=!1)}}async _hasQuickCompleteDefaults(){try{let t=((await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId})).tasks||[]).find(i=>i.id===this._taskId);return!!t?.quick_complete_defaults&&Object.keys(t.quick_complete_defaults).length>0}catch{return!1}}_setUrlMode(e){this._urlMode!==e&&(this._urlMode=e,this._generate())}_print(){if(!this._viewResult)return;let e=this._viewResult,t=e.label.task_name?`${e.label.object_name} \u2014 ${e.label.task_name}`:e.label.object_name,i=[e.label.manufacturer,e.label.model].filter(Boolean).join(" "),a=window.open("","_blank","width=600,height=500");if(!a)return;let o=this._dialogLang,d=qe(t),h=qe(i),_=!!this._completeResult,m=!!this._quickResult,b=qe(n("qr_action_view",o)),k=qe(n("qr_action_complete",o)),w=qe(n("qr_action_quick_complete",o));a.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||
`],c([f({attribute:!1})],W.prototype,"hass",2),c([u()],W.prototype,"_open",2),c([u()],W.prototype,"_saving",2),c([u()],W.prototype,"_error",2),c([u()],W.prototype,"_draft",2),c([u()],W.prototype,"_partOptions",2),c([u()],W.prototype,"_partQty",2),c([u()],W.prototype,"_readingRows",2),c([u()],W.prototype,"_readingText",2);customElements.get("maintenance-history-edit-dialog")||customElements.define("maintenance-history-edit-dialog",W)});function qe(r){return r.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function $s(r){return!r.startsWith("data:image/svg+xml,")&&!r.startsWith("data:image/png;base64,")?"":qe(r)}function el(r){return r.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var V,Un=y(()=>{"use strict";R();H();C();Ji();V=class extends E{constructor(){super(...arguments);this.lang="";this._open=!1;this._loading=!1;this._error="";this._viewResult=null;this._completeResult=null;this._quickResult=null;this._urlMode="companion";this._entryId="";this._taskId=null;this._objectName="";this._taskName="";this._generateSeq=0}get _dialogLang(){return this.lang||P(this.hass)}openForObject(e,t){this._entryId=e,this._taskId=null,this._objectName=t,this._taskName="",this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null,this._open=!0,this._generate()}openForTask(e,t,i,a){this._entryId=e,this._taskId=t,this._objectName=i,this._taskName=a,this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null,this._open=!0,this._generate()}async _generate(){let e=++this._generateSeq;this._loading=!0,this._error="",this._viewResult=null,this._completeResult=null,this._quickResult=null;try{let t={type:"maintenance_supporter/qr/generate",entry_id:this._entryId,url_mode:this._urlMode};this._taskId&&(t.task_id=this._taskId);let i=[this.hass.connection.sendMessagePromise({...t,action:"view"})];this._taskId&&(i.push(this.hass.connection.sendMessagePromise({...t,action:"complete"})),await this._hasQuickCompleteDefaults()&&i.push(this.hass.connection.sendMessagePromise({...t,action:"quick_complete"})));let a=await Promise.all(i);if(e!==this._generateSeq)return;this._viewResult=a[0],a.length>1&&(this._completeResult=a[1]),a.length>2&&(this._quickResult=a[2])}catch(t){if(e!==this._generateSeq)return;let i=t?.code,a=t?.message;this._error=i==="no_url"||typeof a=="string"&&a.includes("No Home Assistant URL")?n("qr_error_no_url",this._dialogLang):n("qr_error",this._dialogLang)}finally{e===this._generateSeq&&(this._loading=!1)}}async _hasQuickCompleteDefaults(){try{let t=((await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:this._entryId})).tasks||[]).find(i=>i.id===this._taskId);return!!t?.quick_complete_defaults&&Object.keys(t.quick_complete_defaults).length>0}catch{return!1}}_setUrlMode(e){this._urlMode!==e&&(this._urlMode=e,this._generate())}_print(){if(!this._viewResult)return;let e=this._viewResult,t=e.label.task_name?`${e.label.object_name} \u2014 ${e.label.task_name}`:e.label.object_name,i=[e.label.manufacturer,e.label.model].filter(Boolean).join(" "),a=window.open("","_blank","width=600,height=500");if(!a)return;let o=this._dialogLang,d=qe(t),h=qe(i),_=!!this._completeResult,m=!!this._quickResult,b=qe(n("qr_action_view",o)),k=qe(n("qr_action_complete",o)),w=qe(n("qr_action_quick_complete",o));a.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
|
||||
<meta name="color-scheme" content="light">
|
||||
<title>${d}</title>
|
||||
<style>
|
||||
@@ -3981,21 +4020,21 @@ Test pressure`,checklist_help:"One step per line. Max 100 items.",err_too_long:"
|
||||
${h?`<div class="sub">${h}</div>`:""}
|
||||
<div class="qr-row">
|
||||
<div class="qr-col">
|
||||
<img src="${ks(this._viewResult.svg_data_uri)}" alt="QR Info" />
|
||||
<img src="${$s(this._viewResult.svg_data_uri)}" alt="QR Info" />
|
||||
<div class="qr-label">${b}</div>
|
||||
</div>
|
||||
${_?`<div class="qr-col">
|
||||
<img src="${ks(this._completeResult.svg_data_uri)}" alt="QR Complete" />
|
||||
<img src="${$s(this._completeResult.svg_data_uri)}" alt="QR Complete" />
|
||||
<div class="qr-label">${k}</div>
|
||||
</div>`:""}
|
||||
${m?`<div class="qr-col">
|
||||
<img src="${ks(this._quickResult.svg_data_uri)}" alt="QR Quick-complete" />
|
||||
<img src="${$s(this._quickResult.svg_data_uri)}" alt="QR Quick-complete" />
|
||||
<div class="qr-label">${w}</div>
|
||||
</div>`:""}
|
||||
</div>
|
||||
<div class="url">${qe(this._viewResult.url)}</div>
|
||||
<script>setTimeout(()=>window.print(),300)<\/script>
|
||||
</body></html>`),a.document.close()}_downloadSvg(e,t){let i=decodeURIComponent(e.svg_data_uri.replace("data:image/svg+xml,","")),a=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;jr(i,`qr-${Qo(a)}-${t}.svg`,"image/svg+xml")}_close(){this._open=!1,this._viewResult=null,this._completeResult=null,this._quickResult=null,this._error="",this._loading=!1}render(){if(!this._open)return l``;let e=this._dialogLang,t=this._taskName?`${n("qr_code",e)}: ${this._objectName} \u2014 ${this._taskName}`:`${n("qr_code",e)}: ${this._objectName}`,i=!!this._viewResult;return l`
|
||||
</body></html>`),a.document.close()}_downloadSvg(e,t){let i=decodeURIComponent(e.svg_data_uri.replace("data:image/svg+xml,","")),a=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;jr(i,`qr-${el(a)}-${t}.svg`,"image/svg+xml")}_close(){this._open=!1,this._viewResult=null,this._completeResult=null,this._quickResult=null,this._error="",this._loading=!1}render(){if(!this._open)return l``;let e=this._dialogLang,t=this._taskName?`${n("qr_code",e)}: ${this._objectName} \u2014 ${this._taskName}`:`${n("qr_code",e)}: ${this._objectName}`,i=!!this._viewResult;return l`
|
||||
<ha-dialog open @closed=${this._close}>
|
||||
<div class="dialog-title">${t}</div>
|
||||
<div class="content">
|
||||
@@ -4071,7 +4110,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}};W.styles=$`
|
||||
`}};V.styles=$`
|
||||
.dialog-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
@@ -4191,9 +4230,9 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
color: var(--text-primary-color, #fff);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
`,c([f({attribute:!1})],W.prototype,"hass",2),c([f()],W.prototype,"lang",2),c([u()],W.prototype,"_open",2),c([u()],W.prototype,"_loading",2),c([u()],W.prototype,"_error",2),c([u()],W.prototype,"_viewResult",2),c([u()],W.prototype,"_completeResult",2),c([u()],W.prototype,"_quickResult",2),c([u()],W.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",W)});function zn(r,s,e=new Date){let t=r?.hours,i=G(r?.snoozed_until);if(typeof t!="number"||!Number.isFinite(i))return n("snoozed",s);let a=be(),d=Le(i,a)===Le(e,a)?Zt(new Date(i),s):Jt(r.snoozed_until,s);return(t===1?n("snoozed_for_one",s):n("snoozed_for",s).replace("{hours}",I(t,s))).replace("{until}",d)}var Fn=y(()=>{"use strict";C();ee()});function yi(r){return r?customElements.get("ha-markdown")?l`<ha-markdown class="notes-md" .content=${r} breaks></ha-markdown>`:l`${r}`:p}var $s=y(()=>{"use strict";R()});function Un(r,s,e,t){let i=e,a=h=>typeof h=="string"?h:null,o=h=>typeof h=="number"?h:null,d=a(i.timestamp)??"";return{entry_id:r,task_id:s,original_timestamp:d,type:a(i.type)||"completed",timestamp:d,notes:a(i.notes),cost:o(i.cost),duration:o(i.duration),completed_by:a(i.completed_by),used_parts:Array.isArray(i.used_parts)?i.used_parts:null,photo_doc_ids:di(i),reading_value:o(i.reading_value),reading_values:xe(i),readings:t?.readings??[],task_type:t?.type??null,reading_unit:t?.reading_unit??null}}var Bn=y(()=>{"use strict";pi();Oe()});function Zo(r){return r&&Wn(r.ref_no)?String(r.ref_no):null}function Vn(r,s){let e=Zo(r);return e&&s&&Wn(s.ref_no)?`${e}.${s.ref_no}`:null}function Gn(r,s){return r?l`<span class="ref-chip" title=${s??""}>#${r}</span>`:p}var Wn,Es=y(()=>{"use strict";R();Wn=r=>typeof r=="number"&&Number.isInteger(r)&&r>0});var Kn=y(()=>{"use strict"});function Yn(r,s){if(!r)return"";let e=Jo[r.trim()];if(e)return n(e,s);for(let[t,i,a]of Xo){let o=r.trim().match(t);if(o){let d=a(o,s);return n(i,s).replace(/\{(\w+)\}/g,(h,_)=>d[_]??h)}}return r}var Jo,Xo,Qn=y(()=>{"use strict";C();Jo={"Completed from dashboard button":"hist_note_button","Completed from the To-do list":"hist_note_todo","Completed by voice":"hist_note_voice","Completed from the notification":"hist_note_notification","Completed via NFC tag":"hist_note_nfc","Completed from the shopping list":"hist_note_shopping_list","Completed from a mirrored to-do list":"hist_note_todo_mirror","Skipped from notification":"hist_note_skipped_notification","Skipped from dashboard button":"hist_note_skipped_button","Sensor trigger activated":"hist_note_sensor_triggered","Initial value set during task creation":"hist_note_initial"},Xo=[[/^Auto-completed: sensor recovered \((.+)\)$/,"hist_note_auto_recovered",r=>({value:r[1]})],[/^Reset to (\d{4}-\d{2}-\d{2})$/,"hist_note_reset",(r,s)=>({date:H(r[1],s)})],[/^Trigger entity replaced: (.+) → (.+)$/,"hist_note_trigger_replaced",r=>({old:r[1],new:r[2]})],[/^Sensor trigger removed \(entity was: (.+)\)\. Schedule converted to (\w+)\.$/,"hist_note_trigger_removed",(r,s)=>({entity:r[1],schedule:n(r[2],s)})],[/^Entity (.+) removed from compound trigger; (\d+) conditions remain\.$/,"hist_note_compound_removed",r=>({entity:r[1],count:r[2]})]]});function Zn(r){let s=e=>{let t=G(e.timestamp);return Number.isNaN(t)?-1/0:t};return(r??[]).map((e,t)=>({entry:e,index:t,at:s(e)})).sort((e,t)=>t.at-e.at||t.index-e.index).map(e=>e.entry)}function tl(r,s){return r.taskRef&&s.ref_no?`${r.taskRef}-${s.ref_no}`:null}function il(r,s){let e=di(s);return e.length>0?l`<div class="history-photos">
|
||||
`,c([f({attribute:!1})],V.prototype,"hass",2),c([f()],V.prototype,"lang",2),c([u()],V.prototype,"_open",2),c([u()],V.prototype,"_loading",2),c([u()],V.prototype,"_error",2),c([u()],V.prototype,"_viewResult",2),c([u()],V.prototype,"_completeResult",2),c([u()],V.prototype,"_quickResult",2),c([u()],V.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",V)});function Bn(r,s,e=new Date){let t=r?.hours,i=K(r?.snoozed_until);if(typeof t!="number"||!Number.isFinite(i))return n("snoozed",s);let a=be(),d=Le(i,a)===Le(e,a)?Jt(new Date(i),s):Xt(r.snoozed_until,s);return(t===1?n("snoozed_for_one",s):n("snoozed_for",s).replace("{hours}",I(t,s))).replace("{until}",d)}var Wn=y(()=>{"use strict";C();ee()});function wi(r){return r?customElements.get("ha-markdown")?l`<ha-markdown class="notes-md" .content=${r} breaks></ha-markdown>`:l`${r}`:p}var Es=y(()=>{"use strict";R()});function Vn(r,s,e,t){let i=e,a=h=>typeof h=="string"?h:null,o=h=>typeof h=="number"?h:null,d=a(i.timestamp)??"";return{entry_id:r,task_id:s,original_timestamp:d,type:a(i.type)||"completed",timestamp:d,notes:a(i.notes),cost:o(i.cost),duration:o(i.duration),completed_by:a(i.completed_by),used_parts:Array.isArray(i.used_parts)?i.used_parts:null,photo_doc_ids:pi(i),reading_value:o(i.reading_value),reading_values:xe(i),readings:t?.readings??[],task_type:t?.type??null,reading_unit:t?.reading_unit??null}}var Gn=y(()=>{"use strict";ui();Oe()});function tl(r){return r&&Kn(r.ref_no)?String(r.ref_no):null}function Yn(r,s){let e=tl(r);return e&&s&&Kn(s.ref_no)?`${e}.${s.ref_no}`:null}function Qn(r,s){return r?l`<span class="ref-chip" title=${s??""}>#${r}</span>`:p}var Kn,Ss=y(()=>{"use strict";R();Kn=r=>typeof r=="number"&&Number.isInteger(r)&&r>0});var Zn=y(()=>{"use strict"});function Jn(r,s){if(!r)return"";let e=il[r.trim()];if(e)return n(e,s);for(let[t,i,a]of sl){let o=r.trim().match(t);if(o){let d=a(o,s);return n(i,s).replace(/\{(\w+)\}/g,(h,_)=>d[_]??h)}}return r}var il,sl,Xn=y(()=>{"use strict";C();il={"Completed from dashboard button":"hist_note_button","Completed from the To-do list":"hist_note_todo","Completed by voice":"hist_note_voice","Completed from the notification":"hist_note_notification","Completed via NFC tag":"hist_note_nfc","Completed from the shopping list":"hist_note_shopping_list","Completed from a mirrored to-do list":"hist_note_todo_mirror","Skipped from notification":"hist_note_skipped_notification","Skipped from dashboard button":"hist_note_skipped_button","Sensor trigger activated":"hist_note_sensor_triggered","Initial value set during task creation":"hist_note_initial"},sl=[[/^Auto-completed: sensor recovered \((.+)\)$/,"hist_note_auto_recovered",r=>({value:r[1]})],[/^Reset to (\d{4}-\d{2}-\d{2})$/,"hist_note_reset",(r,s)=>({date:j(r[1],s)})],[/^Trigger entity replaced: (.+) → (.+)$/,"hist_note_trigger_replaced",r=>({old:r[1],new:r[2]})],[/^Sensor trigger removed \(entity was: (.+)\)\. Schedule converted to (\w+)\.$/,"hist_note_trigger_removed",(r,s)=>({entity:r[1],schedule:n(r[2],s)})],[/^Entity (.+) removed from compound trigger; (\d+) conditions remain\.$/,"hist_note_compound_removed",r=>({entity:r[1],count:r[2]})]]});function ea(r){let s=e=>{let t=K(e.timestamp);return Number.isNaN(t)?-1/0:t};return(r??[]).map((e,t)=>({entry:e,index:t,at:s(e)})).sort((e,t)=>t.at-e.at||t.index-e.index).map(e=>e.entry)}function nl(r,s){return r.taskRef&&s.ref_no?`${r.taskRef}-${s.ref_no}`:null}function al(r,s){let e=pi(s);return e.length>0?l`<div class="history-photos">
|
||||
${e.map(t=>l`<maintenance-history-photo .hass=${r} .docId=${t}></maintenance-history-photo>`)}
|
||||
</div>`:p}function sl(r,s){let e=s.lang,t=o=>I(o,e,{maximumFractionDigits:3}),i=o=>o==null?"":` (${o>=0?"+":""}${t(o)})`,a=xe(r);return a.length>0?l`<div class="history-readings">
|
||||
</div>`:p}function ol(r,s){let e=s.lang,t=o=>I(o,e,{maximumFractionDigits:3}),i=o=>o==null?"":` (${o>=0?"+":""}${t(o)})`,a=xe(r);return a.length>0?l`<div class="history-readings">
|
||||
${a.map(o=>l`<span class="history-reading">
|
||||
<span class="history-reading-name">${o.name}</span>
|
||||
<span class="history-reading-value">${t(o.value)}${o.unit?` ${o.unit}`:""}${i(s.readingSlotDelta?.(r,o.id))}</span>
|
||||
@@ -4201,7 +4240,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
</div>`:r.reading_value!=null?l`<div class="history-readings"><span class="history-reading">
|
||||
<span class="history-reading-name">${n("reading_label",e)}</span>
|
||||
<span class="history-reading-value">${t(r.reading_value)}${s.readingUnit?` ${s.readingUnit}`:""}${i(s.readingDelta?.(r))}</span>
|
||||
</span></div>`:p}function Jn(r,s,e={}){let t=s.lang,{compact:i=!1,showRef:a=!0,showBadges:o=!0,showEdit:d=!0}=e,h=s.openEdit,_=d&&!!h&&el.includes(r.type),m=typeof r.parts_cost=="number"?r.parts_cost:null,b=r.cost_basis==="use",k=b&&!!r.purchase;return l`
|
||||
</span></div>`:p}function ta(r,s,e={}){let t=s.lang,{compact:i=!1,showRef:a=!0,showBadges:o=!0,showEdit:d=!0}=e,h=s.openEdit,_=d&&!!h&&rl.includes(r.type),m=typeof r.parts_cost=="number"?r.parts_cost:null,b=r.cost_basis==="use",k=b&&!!r.purchase;return l`
|
||||
<div class="history-entry${i?" compact":""}">
|
||||
${i?p:l`<div class="history-icon ${r.type}">
|
||||
<ha-icon .icon=${wt[r.type]||"mdi:circle"}></ha-icon>
|
||||
@@ -4209,7 +4248,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
<div class="history-content">
|
||||
<div class="history-row">
|
||||
<strong>${n(r.type,t)}</strong>
|
||||
${a?Gn(tl(s,r),n("ref_number",t)):p}
|
||||
${a?Qn(nl(s,r),n("ref_number",t)):p}
|
||||
${o&&r.phase_id?l`<span class="history-phase-badge">${s.phaseNames?.[r.phase_id]||r.phase_id}</span>`:p}
|
||||
${o&&r.auto?l`<span class="history-auto-badge">${n("history_auto",t)}</span>`:p}
|
||||
${_?l`<button class="history-edit-btn"
|
||||
@@ -4218,38 +4257,38 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
<ha-icon icon="mdi:pencil"></ha-icon>
|
||||
</button>`:p}
|
||||
</div>
|
||||
<div class="history-date">${Jt(r.timestamp,t)}</div>
|
||||
${r.notes?l`<div class="history-notes">${Yn(r.notes,t)}</div>`:p}
|
||||
${il(s.hass,r)}
|
||||
${sl(r,s)}
|
||||
<div class="history-date">${Xt(r.timestamp,t)}</div>
|
||||
${r.notes?l`<div class="history-notes">${Jn(r.notes,t)}</div>`:p}
|
||||
${al(s.hass,r)}
|
||||
${ol(r,s)}
|
||||
${r.cost!=null||r.duration!=null||r.trigger_value!=null||m!=null?l`<div class="history-details">
|
||||
${r.cost!=null?k?l`<span>${n("history_purchase_stock",t).replace("{amount}",re(r.cost,s.currencySymbol,t))}</span>`:r.cost<0?l`<span class="history-credit">${n("cost_kind_credit",t)}: ${re(-r.cost,s.currencySymbol,t)}</span>`:l`<span>${n("cost",t)}: ${re(r.cost,s.currencySymbol,t)}</span>`:p}
|
||||
${m!=null?l`<span title=${n(b?"history_parts_counted_hint":"history_parts_info_hint",t)}>${n(b?"history_parts_counted":"history_parts_info",t).replace("{amount}",re(m,s.currencySymbol,t))}</span>`:p}
|
||||
${r.duration!=null?l`<span>${n("duration",t)}: ${ei(r.duration,t)}</span>`:p}
|
||||
${r.duration!=null?l`<span>${n("duration",t)}: ${ti(r.duration,t)}</span>`:p}
|
||||
${r.trigger_value!=null?l`<span>${n("trigger_val",t)}: ${r.trigger_value}</span>`:p}
|
||||
</div>`:p}
|
||||
</div>
|
||||
</div>
|
||||
`}var el,Xn=y(()=>{"use strict";R();C();ws();pi();Es();Oe();Kn();Qn();ee();el=["completed","reset","skipped"]});function qt(r){return r.archived?"archived":r.is_done?"done":r.status||"ok"}function Ss(r,s){return n(r==="done"?"completed":r,s)}function xi(r){let s=qt(r);return s==="done"?rl:xt[s]||"var(--disabled-color, #9e9e9e)"}function nl(r){return wt[r==="done"?"completed":r]||"mdi:circle-medium"}function ea(r,s,e="pill"){let t=qt(r),i=Ss(t,s);return e==="chip"?l`<span class="status-chip ${t}">${i}</span>`:l`<span class="status-badge ${t}" role="img" title="${i}" aria-label="${i}"><ha-icon icon="${nl(t)}"></ha-icon><span class="status-label">${i}</span></span>`}var rl,Ts=y(()=>{"use strict";R();C();kt();rl="var(--maint-done-color, #78909c)"});var wi,ta=y(()=>{"use strict";wi=class{constructor(){this._handle=null}schedule(s,e=4e3){this.clear(),this._handle=setTimeout(()=>{this._handle=null,s()},e)}clear(){this._handle!==null&&(clearTimeout(this._handle),this._handle=null)}get pending(){return this._handle!==null}}});function ia(r,s){let e=r.interval_analysis,t=e?.weibull_beta,i=e?.weibull_eta;if(t==null||i==null||i<=0)return p;let a=r.interval_days??0,o=r.suggested_interval??a;return l`
|
||||
`}var rl,ia=y(()=>{"use strict";R();C();ks();ui();Ss();Oe();Zn();Xn();ee();rl=["completed","reset","skipped"]});function qt(r){return r.archived?"archived":r.is_done?"done":r.status||"ok"}function Ts(r,s){return n(r==="done"?"completed":r,s)}function ki(r){let s=qt(r);return s==="done"?ll:xt[s]||"var(--disabled-color, #9e9e9e)"}function cl(r){return wt[r==="done"?"completed":r]||"mdi:circle-medium"}function sa(r,s,e="pill"){let t=qt(r),i=Ts(t,s);return e==="chip"?l`<span class="status-chip ${t}">${i}</span>`:l`<span class="status-badge ${t}" role="img" title="${i}" aria-label="${i}"><ha-icon icon="${cl(t)}"></ha-icon><span class="status-label">${i}</span></span>`}var ll,As=y(()=>{"use strict";R();C();kt();ll="var(--maint-done-color, #78909c)"});var $i,ra=y(()=>{"use strict";$i=class{constructor(){this._handle=null}schedule(s,e=4e3){this.clear(),this._handle=setTimeout(()=>{this._handle=null,s()},e)}clear(){this._handle!==null&&(clearTimeout(this._handle),this._handle=null)}get pending(){return this._handle!==null}}});function na(r,s){let e=r.interval_analysis,t=e?.weibull_beta,i=e?.weibull_eta;if(t==null||i==null||i<=0)return p;let a=r.interval_days??0,o=r.suggested_interval??a;return l`
|
||||
<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>
|
||||
${n("weibull_reliability_curve",s)}
|
||||
${al(t,s)}
|
||||
${dl(t,s)}
|
||||
</div>
|
||||
${ol(t,i,a,o,s)}
|
||||
${ll(e,s)}
|
||||
${e?.confidence_interval_low!=null?cl(e,r,s):p}
|
||||
${pl(t,i,a,o,s)}
|
||||
${ul(e,s)}
|
||||
${e?.confidence_interval_low!=null?hl(e,r,s):p}
|
||||
</div>
|
||||
`}function al(r,s){let e,t,i;return r<.8?(e="early_failures",t="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z",i="beta_early_failures"):r<=1.2?(e="random_failures",t="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"):r<=3.5?(e="wear_out",t="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"):(e="highly_predictable",t="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"),l`
|
||||
`}function dl(r,s){let e,t,i;return r<.8?(e="early_failures",t="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z",i="beta_early_failures"):r<=1.2?(e="random_failures",t="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"):r<=3.5?(e="wear_out",t="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"):(e="highly_predictable",t="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"),l`
|
||||
<span class="beta-badge ${e}">
|
||||
<ha-svg-icon path="${t}"></ha-svg-icon>
|
||||
${n(i,s)} (\u03B2=${I(r,s,2)})
|
||||
</span>
|
||||
`}function ol(r,s,e,t,i){let w=Math.max(e,t,s,1)*1.3,v=50,x=[];for(let Z=0;Z<=v;Z++){let se=Z/v*w,xa=1-Math.exp(-Math.pow(se/s,r)),wa=32+se/w*260,ka=136-xa*128;x.push([wa,ka])}let T=x.map(([Z,se])=>`${A(Z)},${A(se)}`).join(" "),N="M32,136 "+x.map(([Z,se])=>`L${A(Z)},${A(se)}`).join(" ")+` L${A(x[v][0])},136 Z`,V=32+e/w*260,ce=1-Math.exp(-Math.pow(e/s,r)),Me=136-ce*128,ba=I((1-ce)*100,i,0),js=32+t/w*260,ya=[0,.25,.5,.75,1];return l`
|
||||
`}function pl(r,s,e,t,i){let w=Math.max(e,t,s,1)*1.3,v=50,x=[];for(let Z=0;Z<=v;Z++){let se=Z/v*w,Ea=1-Math.exp(-Math.pow(se/s,r)),Sa=32+se/w*260,Ta=136-Ea*128;x.push([Sa,Ta])}let T=x.map(([Z,se])=>`${A(Z)},${A(se)}`).join(" "),O="M32,136 "+x.map(([Z,se])=>`L${A(Z)},${A(se)}`).join(" ")+` L${A(x[v][0])},136 Z`,G=32+e/w*260,ce=1-Math.exp(-Math.pow(e/s,r)),Me=136-ce*128,ka=I((1-ce)*100,i,0),js=32+t/w*260,$a=[0,.25,.5,.75,1];return l`
|
||||
<div class="weibull-chart">
|
||||
<svg viewBox="0 0 ${300} ${160}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${n("chart_weibull",i)}">
|
||||
${ya.map(Z=>{let se=136-Z*128;return Ue`
|
||||
${$a.map(Z=>{let se=136-Z*128;return Ue`
|
||||
<line x1="${32}" y1="${A(se)}" x2="${292}" y2="${A(se)}"
|
||||
stroke="var(--divider-color)" stroke-width="0.5" stroke-dasharray="${Z===.5?"4,3":p}" />
|
||||
<text x="${28}" y="${A(se+3)}" fill="var(--secondary-text-color)"
|
||||
@@ -4260,17 +4299,17 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
<text x="${324/2}" y="${156}" fill="var(--secondary-text-color)" font-size="8" text-anchor="middle">${Math.round(w/2)}</text>
|
||||
<text x="${292}" y="${156}" fill="var(--secondary-text-color)" font-size="8" text-anchor="middle">${Math.round(w)}</text>
|
||||
|
||||
<path d="${N}" fill="var(--primary-color, #03a9f4)" opacity="0.08" />
|
||||
<path d="${O}" fill="var(--primary-color, #03a9f4)" opacity="0.08" />
|
||||
<polyline points="${T}" fill="none"
|
||||
stroke="var(--primary-color, #03a9f4)" stroke-width="2" />
|
||||
|
||||
${e>0?Ue`
|
||||
<line x1="${A(V)}" y1="${8}" x2="${A(V)}" y2="${A(136)}"
|
||||
<line x1="${A(G)}" y1="${8}" x2="${A(G)}" y2="${A(136)}"
|
||||
stroke="var(--primary-color, #03a9f4)" stroke-width="1.5" stroke-dasharray="4,3" />
|
||||
<circle cx="${A(V)}" cy="${A(Me)}" r="3"
|
||||
<circle cx="${A(G)}" cy="${A(Me)}" r="3"
|
||||
fill="var(--primary-color, #03a9f4)" />
|
||||
<text x="${A(V+4)}" y="${A(Me-6)}" fill="var(--primary-color, #03a9f4)"
|
||||
font-size="9" font-weight="600">R=${ba}%</text>
|
||||
<text x="${A(G+4)}" y="${A(Me-6)}" fill="var(--primary-color, #03a9f4)"
|
||||
font-size="9" font-weight="600">R=${ka}%</text>
|
||||
`:p}
|
||||
|
||||
${t>0&&t!==e?Ue`
|
||||
@@ -4289,7 +4328,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
${e>0?l`<span class="legend-item"><span class="legend-swatch" style="background:var(--primary-color, #03a9f4); opacity:0.5"></span> ${n("current_interval_marker",i)}</span>`:p}
|
||||
${t>0&&t!==e?l`<span class="legend-item"><span class="legend-swatch" style="background:var(--success-color, #4caf50)"></span> ${n("recommended_marker",i)}</span>`:p}
|
||||
</div>
|
||||
`}function ll(r,s){return l`
|
||||
`}function ul(r,s){return l`
|
||||
<div class="weibull-info-row">
|
||||
<div class="weibull-info-item">
|
||||
<span>${n("characteristic_life",s)}</span>
|
||||
@@ -4302,7 +4341,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
</div>
|
||||
`:p}
|
||||
</div>
|
||||
`}function cl(r,s,e){let t=r.confidence_interval_low,i=r.confidence_interval_high,a=s.suggested_interval??s.interval_days??0,o=s.interval_days??0,d=Math.max(0,t-5),_=i+5-d,m=(t-d)/_*100,b=(i-t)/_*100,k=(a-d)/_*100,w=o>0?(o-d)/_*100:-1;return l`
|
||||
`}function hl(r,s,e){let t=r.confidence_interval_low,i=r.confidence_interval_high,a=s.suggested_interval??s.interval_days??0,o=s.interval_days??0,d=Math.max(0,t-5),_=i+5-d,m=(t-d)/_*100,b=(i-t)/_*100,k=(a-d)/_*100,w=o>0?(o-d)/_*100:-1;return l`
|
||||
<div class="confidence-range">
|
||||
<div class="confidence-range-title">
|
||||
${n("confidence_interval",e)}: ${a} ${n("days",e)} (${t}\u2013${i})
|
||||
@@ -4317,7 +4356,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
<span class="confidence-text high">${n("confidence_aggressive",e)} (${i}${n("days",e).charAt(0)})</span>
|
||||
</div>
|
||||
</div>
|
||||
`}var sa=y(()=>{"use strict";R();C();gi()});function ra(r,s,e){let t=r.degradation_trend!=null&&r.degradation_trend!=="insufficient_data",i=r.days_until_threshold!=null,a=r.environmental_factor!=null&&r.environmental_factor!==1;if(!t&&!i&&!a)return p;let o=r.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":r.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 l`
|
||||
`}var aa=y(()=>{"use strict";R();C();fi()});function oa(r,s,e){let t=r.degradation_trend!=null&&r.degradation_trend!=="insufficient_data",i=r.days_until_threshold!=null,a=r.environmental_factor!=null&&r.environmental_factor!==1;if(!t&&!i&&!a)return p;let o=r.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":r.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 l`
|
||||
<div class="prediction-section">
|
||||
${r.sensor_prediction_urgency?l`
|
||||
<div class="prediction-urgency-banner">
|
||||
@@ -4343,7 +4382,7 @@ ${h?`<div class="sub">${h}</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">${n("days_until_threshold",s)}</span>
|
||||
<span class="prediction-value prediction-days${r.days_until_threshold===0?" exceeded":r.sensor_prediction_urgency?" urgent":""}">${r.days_until_threshold===0?n("threshold_exceeded",s):"~"+Math.round(r.days_until_threshold)+" "+n("days",s)}</span>
|
||||
${r.threshold_prediction_date?l`<span class="prediction-date">${H(r.threshold_prediction_date,s)}</span>`:p}
|
||||
${r.threshold_prediction_date?l`<span class="prediction-date">${j(r.threshold_prediction_date,s)}</span>`:p}
|
||||
${r.threshold_prediction_confidence?l`<span class="confidence-dot ${r.threshold_prediction_confidence}"></span>`:p}
|
||||
${(r.prediction_cycles??0)>0?l`<span class="prediction-cycles">${n("prediction_cycles",s)}: ${r.prediction_cycles}</span>`:p}
|
||||
</div>
|
||||
@@ -4353,12 +4392,12 @@ ${h?`<div class="sub">${h}</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">${n("environmental_adjustment",s)}</span>
|
||||
<span class="prediction-value">${I(r.environmental_factor,s,2)}x</span>
|
||||
${r.environmental_entity?l`<span class="prediction-entity entity-link" @click=${d=>Or(d,r.environmental_entity)}>${r.environmental_entity}</span>`:p}
|
||||
${r.environmental_entity?l`<span class="prediction-entity entity-link" @click=${d=>Hr(d,r.environmental_entity)}>${r.environmental_entity}</span>`:p}
|
||||
</div>
|
||||
`:p}
|
||||
</div>
|
||||
</div>
|
||||
`}var na=y(()=>{"use strict";R();C()});function aa(r,s,e,t){let i=Math.max(r||1,s);return l`
|
||||
`}var la=y(()=>{"use strict";R();C()});function ca(r,s,e,t){let i=Math.max(r||1,s);return l`
|
||||
<div class="interval-comparison">
|
||||
<div class="interval-bar">
|
||||
<div class="interval-label">
|
||||
@@ -4376,7 +4415,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
style="width: ${Math.min(s/i*100,100)}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
`}var oa=y(()=>{"use strict";R();C()});function ca(){return Number(We().slice(5,7))-1}function da(r,s,e){if(!e.seasonal||!r.seasonal_factor||r.seasonal_factor===1)return p;let t=la.map(d=>n(d,s)),i=ca(),a=r.seasonal_factors||r.interval_analysis?.seasonal_factors||null,o=a&&a.length===12?a:t.map((d,h)=>{let _=r.seasonal_factor||1,m=Math.sin((h-6)*Math.PI/6)*.3;return Math.max(.7,Math.min(1.3,_+m))});return l`
|
||||
`}var da=y(()=>{"use strict";R();C()});function ua(){return Number(We().slice(5,7))-1}function ha(r,s,e){if(!e.seasonal||!r.seasonal_factor||r.seasonal_factor===1)return p;let t=pa.map(d=>n(d,s)),i=ua(),a=r.seasonal_factors||r.interval_analysis?.seasonal_factors||null,o=a&&a.length===12?a:t.map((d,h)=>{let _=r.seasonal_factor||1,m=Math.sin((h-6)*Math.PI/6)*.3;return Math.max(.7,Math.min(1.3,_+m))});return l`
|
||||
<div class="seasonal-card-compact">
|
||||
<h4>${n("seasonal_awareness",s)}</h4>
|
||||
<div class="seasonal-mini-chart">
|
||||
@@ -4393,7 +4432,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
<span class="legend-item"><span class="dot high"></span> ${n("longer",s)}</span>
|
||||
</div>
|
||||
</div>
|
||||
`}function pa(r,s){return dl(r,s)}function dl(r,s){let e=r.seasonal_factors??r.interval_analysis?.seasonal_factors;if(!e||e.length!==12)return p;let t=r.interval_analysis?.seasonal_reason,i=ca(),a=300,o=100,d=8,_=o-d-4,m=Math.max(...e,1.5),b=a/12,k=b*.65,w=d+_-1/m*_;return l`
|
||||
`}function _a(r,s){return _l(r,s)}function _l(r,s){let e=r.seasonal_factors??r.interval_analysis?.seasonal_factors;if(!e||e.length!==12)return p;let t=r.interval_analysis?.seasonal_reason,i=ua(),a=300,o=100,d=8,_=o-d-4,m=Math.max(...e,1.5),b=a/12,k=b*.65,w=d+_-1/m*_;return l`
|
||||
<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>
|
||||
@@ -4403,20 +4442,20 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
<svg viewBox="0 0 ${a} ${o}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${n("chart_seasonal",s)}">
|
||||
<line x1="0" y1="${A(w)}" x2="${a}" y2="${A(w)}"
|
||||
stroke="var(--divider-color)" stroke-width="1" stroke-dasharray="4,3" />
|
||||
${e.map((v,x)=>{let T=v/m*_,N=x*b+(b-k)/2,V=d+_-T,ce=x===i,Me=v<1?"var(--success-color, #4caf50)":v>1?"var(--warning-color, #ff9800)":"var(--secondary-text-color)";return Ue`
|
||||
<rect x="${A(N)}" y="${A(V)}"
|
||||
${e.map((v,x)=>{let T=v/m*_,O=x*b+(b-k)/2,G=d+_-T,ce=x===i,Me=v<1?"var(--success-color, #4caf50)":v>1?"var(--warning-color, #ff9800)":"var(--secondary-text-color)";return Ue`
|
||||
<rect x="${A(O)}" y="${A(G)}"
|
||||
width="${A(k)}" height="${A(T)}"
|
||||
fill="${Me}" opacity="${ce?1:.5}" rx="2" />
|
||||
`})}
|
||||
</svg>
|
||||
<div class="seasonal-labels">
|
||||
${la.map((v,x)=>l`<span class="seasonal-label ${x===i?"active-month":""}">${n(v,s)}</span>`)}
|
||||
${pa.map((v,x)=>l`<span class="seasonal-label ${x===i?"active-month":""}">${n(v,s)}</span>`)}
|
||||
</div>
|
||||
</div>
|
||||
`}var la,ua=y(()=>{"use strict";R();C();gi();ee();la=["month_jan","month_feb","month_mar","month_apr","month_may","month_jun","month_jul","month_aug","month_sep","month_oct","month_nov","month_dec"]});var L,ha=y(()=>{"use strict";R();j();C();ke();Fn();vi();ee();ci();ni();$s();Bn();Oe();Es();Xn();Ts();et();Ze();bi();ta();sa();na();oa();rt();ua();L=class extends E{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._taskId=null;this._task=null;this._objectName="";this._taskRef=null;this._busy=!1;this._error="";this._showSkip=!1;this._showReset=!1;this._showPostpone=!1;this._showDetails=!1;this._showAdaptive=!1;this._skipReason="";this._resetDate="";this._postponeDate="";this._features={adaptive:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1};this._toast="";this._toastTimer=new wi;this._access=ue;this._currencySymbol="";this._partsCostMode="purchase";this._loadSeq=0}get _lang(){return P(this.hass)}async openFor(e,t){(e!==this._entryId||t!==this._taskId)&&(this._task=null),this._entryId=e,this._taskId=t,this._error="",this._showSkip=!1,this._showReset=!1,this._showPostpone=!1,this._showAdaptive=!1,this._skipReason="",this._postponeDate="",this._resetDate=We(),this._open=!0,await Promise.all([this._loadTask(),this._loadFeatures()])}async _loadFeatures(){let e=await K(this.hass);this._features={...this._features,...e.features},this._access=e.access,this._currencySymbol=Ne(e.budget??void 0),Qt(e.budget??void 0),this._partsCostMode=e.partsCostMode}close(){this._loadSeq++,this._open=!1,this._task=null,this._error="",this._toastTimer.clear(),this._toast=""}_showToast(e,t){this._toast=e,this._toastTimer.schedule(()=>{this._toast=""},t)}async _loadTask(){let e=this._entryId,t=this._taskId;if(!e||!t)return;let i=++this._loadSeq,a="",o=await q(this,{type:"maintenance_supporter/object",entry_id:e},{onError:h=>{a=h}});if(i!==this._loadSeq)return;if(o===void 0){this._error=a;return}this._objectName=o?.object?.name||"";let d=(o?.tasks||[]).find(h=>h.id===t);this._task=d??null,this._taskRef=Vn(o?.object,d),d||(this._error=n("ws_err_not_found",this._lang))}_runWs(e){return this._error="",q(this,e,{busy:t=>{this._busy=t},onError:t=>{this._error=t}})}_notifyChanged(e){this.dispatchEvent(new CustomEvent("task-action-fired",{detail:{entry_id:this._entryId,task_id:this._taskId,action:e},bubbles:!0,composed:!0}))}_onComplete(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(le(),_e)).then(async({openCompleteDialog:e})=>{let t=this._task,i=[];try{i=(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects",compact:!0})).objects||[]}catch{}e(oi({entryId:this._entryId,taskId:this._taskId,taskName:t.name,task:t,objects:i,lang:this._lang,features:this._features,currencySymbol:this._currencySymbol,partsCostMode:this._partsCostMode}))&&(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})!==void 0&&(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})!==void 0&&(this._notifyChanged("reset"),this.close())}async _onPostponeConfirm(){if(!this._entryId||!this._taskId||!this._postponeDate)return;await this._runWs({type:"maintenance_supporter/task/postpone",entry_id:this._entryId,task_id:this._taskId,until:this._postponeDate})!==void 0&&(this._notifyChanged("postpone"),this.close())}async _onSnooze(){if(!this._entryId||!this._taskId)return;let e=await this._runWs({type:"maintenance_supporter/task/snooze",entry_id:this._entryId,task_id:this._taskId});e!==void 0&&(this._notifyChanged("snooze"),this.dispatchEvent(new CustomEvent("hass-notification",{detail:{message:zn(e,this._lang)},bubbles:!0,composed:!0})),this.close())}_onEdit(){!this._entryId||!this._taskId||Promise.resolve().then(()=>(le(),_e)).then(({openEditTaskDialog:e})=>{e(this._entryId,this._taskId),this.close()})}_onQr(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(le(),_e)).then(({openQrDialog:e})=>{e({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||!await He(this.hass,{title:n("delete",this._lang),message:n("delete_task_confirm",this._lang),confirmText:n("delete",this._lang),danger:!0}))return;await this._runWs({type:"maintenance_supporter/task/delete",entry_id:this._entryId,task_id:this._taskId})!==void 0&&(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})!==void 0&&(this._no
Line truncated
|
||||
`}var pa,ma=y(()=>{"use strict";R();C();fi();ee();pa=["month_jan","month_feb","month_mar","month_apr","month_may","month_jun","month_jul","month_aug","month_sep","month_oct","month_nov","month_dec"]});var N,ga=y(()=>{"use strict";R();H();C();ke();Wn();yi();ee();di();ai();Es();Gn();Oe();Ss();ia();As();et();Ze();xi();ra();aa();la();da();rt();ma();N=class extends E{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._taskId=null;this._task=null;this._objectName="";this._taskRef=null;this._busy=!1;this._error="";this._showSkip=!1;this._showReset=!1;this._showPostpone=!1;this._showDetails=!1;this._showAdaptive=!1;this._skipReason="";this._resetDate="";this._postponeDate="";this._features={adaptive:!1,seasonal:!1,environmental:!1,budget:!1,groups:!1,checklists:!1,schedule_time:!1,completion_actions:!1,places:!1};this._toast="";this._toastTimer=new $i;this._access=ue;this._currencySymbol="";this._partsCostMode="purchase";this._loadSeq=0}get _lang(){return P(this.hass)}async openFor(e,t){(e!==this._entryId||t!==this._taskId)&&(this._task=null),this._entryId=e,this._taskId=t,this._error="",this._showSkip=!1,this._showReset=!1,this._showPostpone=!1,this._showAdaptive=!1,this._skipReason="",this._postponeDate="",this._resetDate=We(),this._open=!0,await Promise.all([this._loadTask(),this._loadFeatures()])}async _loadFeatures(){let e=await B(this.hass);this._features={...this._features,...e.features},this._access=e.access,this._currencySymbol=Ne(e.budget??void 0),Zt(e.budget??void 0),this._partsCostMode=e.partsCostMode}close(){this._loadSeq++,this._open=!1,this._task=null,this._error="",this._toastTimer.clear(),this._toast=""}_showToast(e,t){this._toast=e,this._toastTimer.schedule(()=>{this._toast=""},t)}async _loadTask(){let e=this._entryId,t=this._taskId;if(!e||!t)return;let i=++this._loadSeq,a="",o=await q(this,{type:"maintenance_supporter/object",entry_id:e},{onError:h=>{a=h}});if(i!==this._loadSeq)return;if(o===void 0){this._error=a;return}this._objectName=o?.object?.name||"";let d=(o?.tasks||[]).find(h=>h.id===t);this._task=d??null,this._taskRef=Yn(o?.object,d),d||(this._error=n("ws_err_not_found",this._lang))}_runWs(e){return this._error="",q(this,e,{busy:t=>{this._busy=t},onError:t=>{this._error=t}})}_notifyChanged(e){this.dispatchEvent(new CustomEvent("task-action-fired",{detail:{entry_id:this._entryId,task_id:this._taskId,action:e},bubbles:!0,composed:!0}))}_onComplete(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(le(),_e)).then(async({openCompleteDialog:e})=>{let t=this._task,i=[];try{i=(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects",compact:!0})).objects||[]}catch{}e(li({entryId:this._entryId,taskId:this._taskId,taskName:t.name,task:t,objects:i,lang:this._lang,features:this._features,currencySymbol:this._currencySymbol,partsCostMode:this._partsCostMode}))&&(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})!==void 0&&(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})!==void 0&&(this._notifyChanged("reset"),this.close())}async _onPostponeConfirm(){if(!this._entryId||!this._taskId||!this._postponeDate)return;await this._runWs({type:"maintenance_supporter/task/postpone",entry_id:this._entryId,task_id:this._taskId,until:this._postponeDate})!==void 0&&(this._notifyChanged("postpone"),this.close())}async _onSnooze(){if(!this._entryId||!this._taskId)return;let e=await this._runWs({type:"maintenance_supporter/task/snooze",entry_id:this._entryId,task_id:this._taskId});e!==void 0&&(this._notifyChanged("snooze"),this.dispatchEvent(new CustomEvent("hass-notification",{detail:{message:Bn(e,this._lang)},bubbles:!0,composed:!0})),this.close())}_onEdit(){!this._entryId||!this._taskId||Promise.resolve().then(()=>(le(),_e)).then(({openEditTaskDialog:e})=>{e(this._entryId,this._taskId),this.close()})}_onQr(){!this._entryId||!this._taskId||!this._task||Promise.resolve().then(()=>(le(),_e)).then(({openQrDialog:e})=>{e({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||!await je(this.hass,{title:n("delete",this._lang),message:n("delete_task_confirm",this._lang),confirmText:n("delete",this._lang),danger:!0}))return;await this._runWs({type:"maintenance_supporter/task/delete",entry_id:this._entryId,task_id:this._taskId})!==void 0&&(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})!==void 0&
Line truncated
|
||||
<div class="recommendation-card">
|
||||
<h4>${n("suggested_interval",t)}</h4>
|
||||
${aa(e.interval_days,e.suggested_interval,e.interval_confidence||"medium",t)}
|
||||
${ca(e.interval_days,e.suggested_interval,e.interval_confidence||"medium",t)}
|
||||
<div class="recommendation-actions">
|
||||
${ae(this.hass?.user,this._access)?l`<button class="btn primary qa-apply-suggestion"
|
||||
@click=${this._applySuggestion} ?disabled=${this._busy}>
|
||||
@@ -4436,14 +4475,14 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
<div class="adaptive-stack">
|
||||
${this._toast?l`<div class="toast">${this._toast}</div>`:p}
|
||||
${i?this._renderRecommendation(e):p}
|
||||
${a?ra(e,t,this._features):p}
|
||||
${o?ia(e,t):p}
|
||||
${a?oa(e,t,this._features):p}
|
||||
${o?na(e,t):p}
|
||||
${d?l`
|
||||
${da(e,t,this._features)}
|
||||
${e.seasonal_factors?.length===12||e.interval_analysis?.seasonal_factors?.length===12?pa(e,t):p}
|
||||
${ha(e,t,this._features)}
|
||||
${e.seasonal_factors?.length===12||e.interval_analysis?.seasonal_factors?.length===12?_a(e,t):p}
|
||||
`:p}
|
||||
</div>
|
||||
`}_renderDetails(e){let t=this._lang,i=e.history||[],a=e.history_count??i.length,o=Zn(i).slice(0,20);return l`
|
||||
`}_renderDetails(e){let t=this._lang,i=e.history||[],a=e.history_count??i.length,o=ea(i).slice(0,20);return l`
|
||||
<div class="details">
|
||||
<div class="stats-grid">
|
||||
<div class="stat">
|
||||
@@ -4456,7 +4495,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">${n("avg_duration",t)}</span>
|
||||
<span class="stat-value">${ei(e.average_duration!=null?Math.round(e.average_duration):null,t)}</span>
|
||||
<span class="stat-value">${ti(e.average_duration!=null?Math.round(e.average_duration):null,t)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="history-header">
|
||||
@@ -4465,7 +4504,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
</div>
|
||||
${i.length===0?l`<div class="history-empty">${n("history_empty",t)}</div>`:l`
|
||||
<div class="history-list">
|
||||
${o.map(d=>Jn(d,{lang:t,hass:this.hass,currencySymbol:this._currencySymbol,openEdit:ae(this.hass?.user,this._access)?h=>this._onEditHistoryEntry(h):void 0,readingUnit:e.reading_unit,readingSlotDelta:(h,_)=>Gr(i,h,_),taskRef:this._taskRef},{compact:!0}))}
|
||||
${o.map(d=>ta(d,{lang:t,hass:this.hass,currencySymbol:this._currencySymbol,openEdit:ae(this.hass?.user,this._access)?h=>this._onEditHistoryEntry(h):void 0,readingUnit:e.reading_unit,readingSlotDelta:(h,_)=>Kr(i,h,_),taskRef:this._taskRef},{compact:!0}))}
|
||||
${a>o.length?l`<div class="history-more">… +${a-o.length} ${n("older_entries",t)}</div>`:p}
|
||||
</div>
|
||||
`}
|
||||
@@ -4474,20 +4513,20 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
${t?l`
|
||||
<div class="header">
|
||||
<div class="title">
|
||||
<span class="status-dot" style="background: ${xi(t)}"></span>
|
||||
<span class="status-dot" style="background: ${ki(t)}"></span>
|
||||
<span class="task-name">${t.name}</span>
|
||||
${ea(t,e)}
|
||||
${sa(t,e)}
|
||||
</div>
|
||||
<div class="object">
|
||||
<button class="link-inline" @click=${()=>{this._entryId&&Promise.resolve().then(()=>(le(),_e)).then(({openObjectQuickActions:o})=>{o(this._entryId),this.close()})}}>${this._objectName}</button>
|
||||
</div>
|
||||
<div class="quick-info">
|
||||
${t.next_due?l`<span><strong>${n("next_due",e)}:</strong> ${H(t.next_due,e)}</span>`:p}
|
||||
${t.last_performed?l`<span><strong>${n("last_performed",e)}:</strong> ${H(t.last_performed,e)}</span>`:p}
|
||||
${t.schedule?.kind&&!["manual","one_time"].includes(t.schedule.kind)||t.interval_days!=null?l`<span><strong>${n("interval",e)}:</strong> ${Nr(t,e)}</span>`:p}
|
||||
${t.next_due?l`<span><strong>${n("next_due",e)}:</strong> ${j(t.next_due,e)}</span>`:p}
|
||||
${t.last_performed?l`<span><strong>${n("last_performed",e)}:</strong> ${j(t.last_performed,e)}</span>`:p}
|
||||
${t.schedule?.kind&&!["manual","one_time"].includes(t.schedule.kind)||t.interval_days!=null?l`<span><strong>${n("interval",e)}:</strong> ${Or(t,e)}</span>`:p}
|
||||
${ye(t)?l`<span><strong>${n("phase_current",e)}:</strong> ${ye(t)}</span>`:p}
|
||||
</div>
|
||||
${t.notes?l`<div class="notes-body">${yi(t.notes)}</div>`:p}
|
||||
${t.notes?l`<div class="notes-body">${wi(t.notes)}</div>`:p}
|
||||
</div>
|
||||
|
||||
${this._error?l`<div class="error">${this._error}</div>`:p}
|
||||
@@ -4614,7 +4653,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
</div>
|
||||
`}
|
||||
`:this._error?l`<div class="error">${this._error}</div>`:l`<div class="loading">${n("loading",e)}</div>`}
|
||||
`)}updated(e){e.has("_open")&&this._open&&ct(this.shadowRoot)}};L.styles=[ti,ot,$`
|
||||
`)}updated(e){e.has("_open")&&this._open&&ct(this.shadowRoot)}};N.styles=[ii,ot,$`
|
||||
:host { display: contents; --ms-modal-max-width: 460px; }
|
||||
.header { display: flex; flex-direction: column; gap: 6px; }
|
||||
.title { display: flex; align-items: center; gap: 10px; }
|
||||
@@ -4772,7 +4811,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
.weibull-section, .seasonal-card-compact { max-width: 100%; }
|
||||
.weibull-chart svg { max-width: 100%; height: auto; }
|
||||
.details-toggle { gap: 12px; flex-wrap: wrap; }
|
||||
`],c([f({attribute:!1})],L.prototype,"hass",2),c([u()],L.prototype,"_open",2),c([u()],L.prototype,"_entryId",2),c([u()],L.prototype,"_taskId",2),c([u()],L.prototype,"_task",2),c([u()],L.prototype,"_objectName",2),c([u()],L.prototype,"_taskRef",2),c([u()],L.prototype,"_busy",2),c([u()],L.prototype,"_error",2),c([u()],L.prototype,"_showSkip",2),c([u()],L.prototype,"_showReset",2),c([u()],L.prototype,"_showPostpone",2),c([u()],L.prototype,"_showDetails",2),c([u()],L.prototype,"_showAdaptive",2),c([u()],L.prototype,"_skipReason",2),c([u()],L.prototype,"_resetDate",2),c([u()],L.prototype,"_postponeDate",2),c([u()],L.prototype,"_features",2),c([u()],L.prototype,"_toast",2),c([u()],L.prototype,"_access",2),c([u()],L.prototype,"_partsCostMode",2);customElements.get("maintenance-task-quick-actions-dialog")||customElements.define("maintenance-task-quick-actions-dialog",L)});var ie,_a=y(()=>{"use strict";R();es();$s();j();C();ke();Ts();et();Ze();bi();vi();ie=class extends E{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._data=null;this._busy=!1;this._error="";this._access=ue}get _lang(){return P(this.hass)}async openFor(e){this._entryId=e,this._error="",this._open=!0,await Promise.all([this._load(),K(this.hass).then(t=>{this._access=t.access})])}close(){this._open=!1,this._data=null,this._error=""}async _load(){if(!this._entryId)return;let e=await q(this,{type:"maintenance_supporter/object",entry_id:this._entryId},{onError:t=>{this._error=t}});e!==void 0&&(this._data=e)}_runWs(e){return this._error="",q(this,e,{busy:t=>{this._busy=t},onError:t=>{this._error=t}})}_onEditObject(){!this._entryId||!this._data||Promise.resolve().then(()=>(le(),_e)).then(({openEditObjectDialog:e})=>{e(this._entryId,this._data.object),this.close()})}_onAddTask(){this._entryId&&Promise.resolve().then(()=>(le(),_e)).then(({openCreateTaskDialog:e})=>{e(this._entryId),this.close()})}async _onDelete(){if(!this._entryId||!this._data||!await He(this.hass,{title:n("delete",this._lang),message:n("delete_object_confirm",this._lang),confirmText:n("delete",this._lang),danger:!0}))return;let t=this._entryId;await this._runWs({type:"maintenance_supporter/object/delete",entry_id:t})!==void 0&&(this.dispatchEvent(new CustomEvent("object-deleted",{detail:{entry_id:t},bubbles:!0,composed:!0})),this.close())}async _onArchiveObject(){if(!this._entryId||!this._data)return;let e=!!this._data.object.archived;if(!e&&!await He(this.hass,{title:n("archive_object",this._lang),message:n("confirm_archive_object",this._lang),confirmText:n("archive_object",this._lang)}))return;let t=this._entryId;await this._runWs({type:e?"maintenance_supporter/object/unarchive":"maintenance_supporter/object/archive",entry_id:t})!==void 0&&(this.dispatchEvent(new CustomEvent("object-changed",{detail:{entry_id:t},bubbles:!0,composed:!0})),this.close())}_onTaskClick(e){this._entryId&&Promise.resolve().then(()=>(le(),_e)).then(({openTaskQuickActions:t})=>{t(this._entryId,e)})}render(){if(!this._open)return p;let e=this._lang,t=this._data,i=t?.object,a=t?.tasks||[],o=ae(this.hass?.user,this._access);return lt(()=>this.close(),l`
|
||||
`],c([f({attribute:!1})],N.prototype,"hass",2),c([u()],N.prototype,"_open",2),c([u()],N.prototype,"_entryId",2),c([u()],N.prototype,"_taskId",2),c([u()],N.prototype,"_task",2),c([u()],N.prototype,"_objectName",2),c([u()],N.prototype,"_taskRef",2),c([u()],N.prototype,"_busy",2),c([u()],N.prototype,"_error",2),c([u()],N.prototype,"_showSkip",2),c([u()],N.prototype,"_showReset",2),c([u()],N.prototype,"_showPostpone",2),c([u()],N.prototype,"_showDetails",2),c([u()],N.prototype,"_showAdaptive",2),c([u()],N.prototype,"_skipReason",2),c([u()],N.prototype,"_resetDate",2),c([u()],N.prototype,"_postponeDate",2),c([u()],N.prototype,"_features",2),c([u()],N.prototype,"_toast",2),c([u()],N.prototype,"_access",2),c([u()],N.prototype,"_partsCostMode",2);customElements.get("maintenance-task-quick-actions-dialog")||customElements.define("maintenance-task-quick-actions-dialog",N)});var ie,fa=y(()=>{"use strict";R();ts();Es();H();C();ke();As();et();Ze();xi();yi();ie=class extends E{constructor(){super(...arguments);this._open=!1;this._entryId=null;this._data=null;this._busy=!1;this._error="";this._access=ue}get _lang(){return P(this.hass)}async openFor(e){this._entryId=e,this._error="",this._open=!0,await Promise.all([this._load(),B(this.hass).then(t=>{this._access=t.access})])}close(){this._open=!1,this._data=null,this._error=""}async _load(){if(!this._entryId)return;let e=await q(this,{type:"maintenance_supporter/object",entry_id:this._entryId},{onError:t=>{this._error=t}});e!==void 0&&(this._data=e)}_runWs(e){return this._error="",q(this,e,{busy:t=>{this._busy=t},onError:t=>{this._error=t}})}_onEditObject(){!this._entryId||!this._data||Promise.resolve().then(()=>(le(),_e)).then(({openEditObjectDialog:e})=>{e(this._entryId,this._data.object),this.close()})}_onAddTask(){this._entryId&&Promise.resolve().then(()=>(le(),_e)).then(({openCreateTaskDialog:e})=>{e(this._entryId),this.close()})}async _onDelete(){if(!this._entryId||!this._data||!await je(this.hass,{title:n("delete",this._lang),message:n("delete_object_confirm",this._lang),confirmText:n("delete",this._lang),danger:!0}))return;let t=this._entryId;await this._runWs({type:"maintenance_supporter/object/delete",entry_id:t})!==void 0&&(this.dispatchEvent(new CustomEvent("object-deleted",{detail:{entry_id:t},bubbles:!0,composed:!0})),this.close())}async _onArchiveObject(){if(!this._entryId||!this._data)return;let e=!!this._data.object.archived;if(!e&&!await je(this.hass,{title:n("archive_object",this._lang),message:n("confirm_archive_object",this._lang),confirmText:n("archive_object",this._lang)}))return;let t=this._entryId;await this._runWs({type:e?"maintenance_supporter/object/unarchive":"maintenance_supporter/object/archive",entry_id:t})!==void 0&&(this.dispatchEvent(new CustomEvent("object-changed",{detail:{entry_id:t},bubbles:!0,composed:!0})),this.close())}_onTaskClick(e){this._entryId&&Promise.resolve().then(()=>(le(),_e)).then(({openTaskQuickActions:t})=>{t(this._entryId,e)})}render(){if(!this._open)return p;let e=this._lang,t=this._data,i=t?.object,a=t?.tasks||[],o=ae(this.hass?.user,this._access);return lt(()=>this.close(),l`
|
||||
${t&&i?l`
|
||||
<div class="header">
|
||||
<div class="title">${i.name}</div>
|
||||
@@ -4790,9 +4829,9 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
<div class="task-list">
|
||||
${a.map(d=>l`
|
||||
<div class="task-row" @click=${()=>this._onTaskClick(d.id)}>
|
||||
<span class="status-dot" style="background: ${xi(d)}"></span>
|
||||
<span class="status-dot" style="background: ${ki(d)}"></span>
|
||||
<span class="task-name">${d.name}</span>
|
||||
<span class="task-status ${qt(d)}">${Ss(qt(d),e)}</span>
|
||||
<span class="task-status ${qt(d)}">${Ts(qt(d),e)}</span>
|
||||
</div>
|
||||
`)}
|
||||
</div>
|
||||
@@ -4802,7 +4841,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
${i.notes?l`
|
||||
<div class="notes-section">
|
||||
<strong>${n("object_notes_label",e)}</strong>
|
||||
<div class="notes-body">${yi(i.notes)}</div>
|
||||
<div class="notes-body">${wi(i.notes)}</div>
|
||||
</div>
|
||||
`:p}
|
||||
|
||||
@@ -4832,7 +4871,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
${i.map(([a,o])=>l`
|
||||
<div class="meta-item">
|
||||
<span class="meta-label">${a}</span>
|
||||
<span class="meta-value">${ii(o)?l`<a href="${o}" target="_blank" rel="noopener noreferrer">${o}</a>`:o}</span>
|
||||
<span class="meta-value">${si(o)?l`<a href="${o}" target="_blank" rel="noopener noreferrer">${o}</a>`:o}</span>
|
||||
</div>
|
||||
`)}
|
||||
</div>
|
||||
@@ -4881,7 +4920,7 @@ ${h?`<div class="sub">${h}</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; }
|
||||
`],c([f({attribute:!1})],ie.prototype,"hass",2),c([u()],ie.prototype,"_open",2),c([u()],ie.prototype,"_entryId",2),c([u()],ie.prototype,"_data",2),c([u()],ie.prototype,"_busy",2),c([u()],ie.prototype,"_error",2),c([u()],ie.prototype,"_access",2);customElements.get("maintenance-object-quick-actions-dialog")||customElements.define("maintenance-object-quick-actions-dialog",ie)});var _e={};Ea(_e,{__resetSettingsCacheForTests:()=>vl,getRowActionStyle:()=>fl,openCompleteDialog:()=>wl,openCreateObjectDialog:()=>As,openCreateTaskDialog:()=>Cs,openEditObjectDialog:()=>bl,openEditTaskDialog:()=>yl,openHistoryEditDialog:()=>xl,openObjectQuickActions:()=>$l,openQrDialog:()=>kl,openTaskQuickActions:()=>Rs});function ki(){return document.querySelector("home-assistant")?.hass}function gl(){return document.querySelector("home-assistant")?.shadowRoot??document.body}function me(r){let s=gl(),e=s.querySelector(r)??document.body.querySelector(r);return e?e.parentNode!==s&&s.appendChild(e):(e=document.createElement(r),s.appendChild(e)),e}function ge(r){let s=ki();if(!s)return!1;r.hass=s;let e=P(s);return Et(e)||pe(e).then(()=>{r.requestUpdate?.()}),Yi(s.locale,s.config?.country,s.config?.time_zone),!0}function fl(r){return K(r).then(s=>s.rowActionStyle)}function vl(){on()}function As(){let r=me(ma);return ge(r)?(r.openCreate(),!0):!1}function bl(r,s){let e=me(ma);return ge(e)?(e.openEdit(r,s),!0):!1}function Cs(r="",s){let e=me(ga);if(!ge(e))return!1;let t=ki();return t?((async()=>{let i=await K(t),a=e;fa(a,i),a.openCreate(r,s)})(),!0):!1}function fa(r,s){r.checklistsEnabled=s.features.checklists,r.scheduleTimeEnabled=s.features.schedule_time,r.completionActionsEnabled=s.features.completion_actions,r.adaptiveFeature=s.features.adaptive,r.seasonalFeature=s.features.seasonal,r.environmentalFeature=s.features.environmental,r.defaultWarningDays=s.defaultWarningDays}function yl(r,s){let e=me(ga);if(!ge(e))return!1;let t=ki();return t?((async()=>{try{let[i,a]=await Promise.all([t.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:r}),K(t)]),o=(i.tasks||[]).find(h=>h.id===s);if(!o){console.warn(`openEditTaskDialog: task ${s} not found in entry ${r}`);return}let d=e;fa(d,a),await d.openEdit(r,o)}catch(i){console.warn("openEditTaskDialog: failed to load task/features",i)}})(),!0):!1}function xl(r){let s=me(pl);return ge(s)?(s.openEdit(r),!0):!1}function wl(r){let s=me(ul);return ge(s)?(li(s,r,P(ki())),!0):!1}function kl(r){let s=me(hl);return ge(s)?(s.openForTask(r.entry_id,r.task_id,r.object_name,r.task_name),!0):!1}function Rs(r,s){let e=me(_l);return ge(e)?(e.openFor(r,s),!0):!1}function $l(r){let s=me(ml);return ge(s)?(s.openFor(r),!0):!1}var ma,ga,pl,ul,hl,_l,ml,le=y(()=>{"use strict";$n();On();gs();Dn();Mn();ha();_a();C();ci();et();ma="maintenance-object-dialog",ga="maintenance-task-dialog",pl="maintenance-history-edit-dialog",ul="maintenance-complete-dialog",hl="maintenance-qr-dialog",_l="maintenance-task-quick-actions-dialog",ml="maintenance-object-quick-actions-dialog"});R();var Da=["assignee_pool","required_completion_fields","checklist","labels","mirror_todo_entities","next_event_titles","history","readings"],Ma=["checklist_progress"],za=["tasks","parts"],Fa=["manual_docs","battery_fleet_excluded"];function Hi(r,s,e=[]){for(let t of s)r[t]===void 0&&(r[t]=[]);for(let t of e)r[t]===void 0&&(r[t]={})}function Ua(r){let s=r;Hi(s,za),s.object&&typeof s.object=="object"&&Hi(s.object,Fa);for(let e of s.tasks)Hi(e,Da,Ma);return r}function yt(r){for(let s of r)Ua(s);return r}function Ba(r,s){if(s.objects)return s.objects;let e=s.delta||[],t=s.removed||[];if(!e.length&&!t.length)return null;let i=new Map(r.map(a=>[a.entry_id,a]));for(let a of e)i.set(a.entry_id,a);for(let a of t)i.delete(a);return[...i.values()]}function sr(r,s){return s.objects&&yt(s.objects),s.delta&&yt(s.delta),Ba(r,s)}j();C();Xi();function qr(r){return(r.tags||[]).includes("photo")}es();function Dr(){return window.customCards??[]}function Ye(r){let s=window;s.customCards=s.customCards||[],s.customCards.some(e=>e.type===r.type)||s.customCards.push(r)}function Mr(r,s,e=t=>import(t)){let t=window,i;try{let m=new URL(s);i=m.origin+m.pathname}catch{return}let a=`__msCardHeal:${r[0]}:${i}`;if(t[a])return;t[a]=!0;let o=0,d=0,h=!1,_=()=>{if(o+=1,r.some(b=>!customElements.get(b))&&d<3&&!h){d+=1,h=!0;try{let b=new URL(s);b.searchParams.set("heal",`${Date.now()}`),e(b.href).catch(()=>{}).finally(()=>{h=!1})}catch{return}}o<20&&window.setTimeout(_,o<8?500:2e3)};window.setTimeout(_,250)}St();var co={"maintenance-supporter-card":[null,"picker_desc_card"],"maintenance-supporter-calendar-card":["picker_name_calendar","picker_desc_calendar"],"maintenance-supporter-panel-card":["picker_name_panel","picker_desc_panel"],"maintenance-budget-section-card":["picker_name_budget","picker_desc_budget"],"maintenance-groups-section-card":["picker_name_groups","picker_desc_groups"],"maintenance-vacation-section-card":["picker_name_vacation","picker_desc_vacation"],
Line truncated
|
||||
`],c([f({attribute:!1})],ie.prototype,"hass",2),c([u()],ie.prototype,"_open",2),c([u()],ie.prototype,"_entryId",2),c([u()],ie.prototype,"_data",2),c([u()],ie.prototype,"_busy",2),c([u()],ie.prototype,"_error",2),c([u()],ie.prototype,"_access",2);customElements.get("maintenance-object-quick-actions-dialog")||customElements.define("maintenance-object-quick-actions-dialog",ie)});var _e={};Ca(_e,{__resetSettingsCacheForTests:()=>wl,getRowActionStyle:()=>xl,openCompleteDialog:()=>Sl,openCreateObjectDialog:()=>Cs,openCreateTaskDialog:()=>Rs,openEditObjectDialog:()=>kl,openEditTaskDialog:()=>$l,openHistoryEditDialog:()=>El,openObjectQuickActions:()=>Al,openQrDialog:()=>Tl,openTaskQuickActions:()=>Is});function Dt(){return document.querySelector("home-assistant")?.hass}function yl(){return document.querySelector("home-assistant")?.shadowRoot??document.body}function me(r){let s=yl(),e=s.querySelector(r)??document.body.querySelector(r);return e?e.parentNode!==s&&s.appendChild(e):(e=document.createElement(r),s.appendChild(e)),e}function ge(r){let s=Dt();if(!s)return!1;r.hass=s;let e=P(s);return Et(e)||pe(e).then(()=>{r.requestUpdate?.()}),Qi(s.locale,s.config?.country,s.config?.time_zone),!0}function xl(r){return B(r).then(s=>s.rowActionStyle)}function wl(){ln()}function Cs(){let r=me(va);return ge(r)?(r.openCreate(),ya(r),!0):!1}async function ya(r){let s=Dt();s&&(r.placesEnabled=(await B(s)).features.places)}function kl(r,s){let e=me(va);return ge(e)?(e.openEdit(r,s),ya(e),!0):!1}function Rs(r="",s){let e=me(ba);if(!ge(e))return!1;let t=Dt();return t?((async()=>{let i=await B(t),a=e;xa(a,i),a.openCreate(r,s)})(),!0):!1}function xa(r,s){r.checklistsEnabled=s.features.checklists,r.scheduleTimeEnabled=s.features.schedule_time,r.completionActionsEnabled=s.features.completion_actions,r.adaptiveFeature=s.features.adaptive,r.seasonalFeature=s.features.seasonal,r.environmentalFeature=s.features.environmental,r.defaultWarningDays=s.defaultWarningDays}function $l(r,s){let e=me(ba);if(!ge(e))return!1;let t=Dt();return t?((async()=>{try{let[i,a]=await Promise.all([t.connection.sendMessagePromise({type:"maintenance_supporter/object",entry_id:r}),B(t)]),o=(i.tasks||[]).find(h=>h.id===s);if(!o){console.warn(`openEditTaskDialog: task ${s} not found in entry ${r}`);return}let d=e;xa(d,a),await d.openEdit(r,o)}catch(i){console.warn("openEditTaskDialog: failed to load task/features",i)}})(),!0):!1}function El(r){let s=me(ml);return ge(s)?(s.openEdit(r),!0):!1}function Sl(r){let s=me(gl);return ge(s)?(ci(s,r,P(Dt())),!0):!1}function Tl(r){let s=me(fl);return ge(s)?(s.openForTask(r.entry_id,r.task_id,r.object_name,r.task_name),!0):!1}function Is(r,s){let e=me(vl);return ge(e)?(e.openFor(r,s),!0):!1}function Al(r){let s=me(bl);return ge(s)?(s.openFor(r),!0):!1}var va,ba,ml,gl,fl,vl,bl,le=y(()=>{"use strict";En();qn();fs();Fn();Un();ga();fa();C();di();et();va="maintenance-object-dialog",ba="maintenance-task-dialog",ml="maintenance-history-edit-dialog",gl="maintenance-complete-dialog",fl="maintenance-qr-dialog",vl="maintenance-task-quick-actions-dialog",bl="maintenance-object-quick-actions-dialog"});R();var Ua=["assignee_pool","required_completion_fields","checklist","labels","mirror_todo_entities","next_event_titles","history","readings"],Ba=["checklist_progress"],Wa=["tasks","parts"],Va=["manual_docs","battery_fleet_excluded"];function qi(r,s,e=[]){for(let t of s)r[t]===void 0&&(r[t]=[]);for(let t of e)r[t]===void 0&&(r[t]={})}function Ga(r){let s=r;qi(s,Wa),s.object&&typeof s.object=="object"&&qi(s.object,Va);for(let e of s.tasks)qi(e,Ua,Ba);return r}function yt(r){for(let s of r)Ga(s);return r}function Ka(r,s){if(s.objects)return s.objects;let e=s.delta||[],t=s.removed||[];if(!e.length&&!t.length)return null;let i=new Map(r.map(a=>[a.entry_id,a]));for(let a of e)i.set(a.entry_id,a);for(let a of t)i.delete(a);return[...i.values()]}function rr(r,s){return s.objects&&yt(s.objects),s.delta&&yt(s.delta),Ka(r,s)}H();C();es();function Dr(r){return(r.tags||[]).includes("photo")}ts();function Mr(){return window.customCards??[]}function Ye(r){let s=window;s.customCards=s.customCards||[],s.customCards.some(e=>e.type===r.type)||s.customCards.push(r)}function zr(r,s,e=t=>import(t)){let t=window,i;try{let m=new URL(s);i=m.origin+m.pathname}catch{return}let a=`__msCardHeal:${r[0]}:${i}`;if(t[a])return;t[a]=!0;let o=0,d=0,h=!1,_=()=>{if(o+=1,r.some(b=>!customElements.get(b))&&d<3&&!h){d+=1,h=!0;try{let b=new URL(s);b.searchParams.set("heal",`${Date.now()}`),e(b.href).catch(()=>{}).finally(()=>{h=!1})}catch{return}}o<20&&window.setTimeout(_,o<8?500:2e3)};window.setTimeout(_,250)}St();var _o={"maintenance-supporter-card":[null,"picker_desc_card"],"maintenance-supporter-calendar-card":["picker_name_calendar","picker_desc_calendar"],"maintenance-supporter-panel-card":["picker_name_panel","picker_desc_panel"],"maintenance-budget-section-card":["picker_name_budget","picker_desc_budget"],"maintenance-groups-section-card":["picker_name_groups","picker_desc
Line truncated
|
||||
<div class="editor">
|
||||
<ha-textfield
|
||||
label="${n("card_title",e)}"
|
||||
@@ -4893,7 +4932,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
<div class="field">
|
||||
<div class="field-label">${n("card_filter_status",e)}</div>
|
||||
<div class="chip-row">
|
||||
${Vt.map(v=>l`
|
||||
${Gt.map(v=>l`
|
||||
<label class="chip ${t.has(v)?"active":""}">
|
||||
<input type="checkbox"
|
||||
.checked=${t.has(v)}
|
||||
@@ -5140,7 +5179,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
font-size: 14px;
|
||||
max-width: 320px;
|
||||
}
|
||||
`,c([f({attribute:!1})],oe.prototype,"hass",2),c([u()],oe.prototype,"_config",2),c([u()],oe.prototype,"_objects",2),c([u()],oe.prototype,"_loadingObjects",2),c([u()],oe.prototype,"_loadError",2),c([u()],oe.prototype,"_views",2);customElements.get("maintenance-supporter-card-editor")||customElements.define("maintenance-supporter-card-editor",oe);gs();C();async function jo(r,s,e,t){try{let i=await r.connection.sendMessagePromise({type:"maintenance_supporter/task/undo",entry_id:s,task_id:e});return{ok:!0,message:n(i?.action_ran?"undo_done_action_ran":"undo_done",t)}}catch(i){let a=i?.code;return a==="changed_since"?{ok:!1,message:n("undo_changed",t)}:a==="nothing_to_undo"?{ok:!1,message:n("undo_nothing",t)}:{ok:!1,message:n("action_error",t)}}}function kn(r,s,e,t,i){let a=(o,d)=>r.dispatchEvent(new CustomEvent("hass-notification",{detail:d?{message:o,action:d,duration:1e4}:{message:o},bubbles:!0,composed:!0}));a(n("task_completed_named",t).replace("{task}",e.taskName),{text:n("undo",t),action:()=>{jo(s,e.entryId,e.taskId,t).then(o=>{a(o.message),o.ok&&i()})}})}R();j();R();j();C();_s();ke();Ze();et();ee();gi();var z=class z extends E{constructor(){super(...arguments);this.flat=!1;this._ov=null;this._loading=!1;this._marking=!1;this._error="";this._history=null;this._rosterSort=z._storedSort();this._typeFilter=null;this._recorded=[];this._access=ue;this._historyRequested=!1;this._localeReady=!1;this._markAll=async()=>{await this._mark(void 0)};this._repair=async()=>{await this._act({type:"maintenance_supporter/battery_fleet/setup",language:this._lang})};this._loadHistory=async e=>{let t=e.target.open;if(jt(Ee.batteryRosterOpen,t?"1":"0"),!(!t||this._historyRequested)){this._historyRequested=!0;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/overview_history"});this._history=i.series}catch{this._history=null}}}}get _lang(){return P(this.hass)}get _canWrite(){return ae(this.hass?.user,this._access)}connectedCallback(){super.connectedCallback(),this.hass&&this._load()}updated(e){Yt(this,e),e.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,pe(this._lang).then(()=>this.requestUpdate()),K(this.hass).then(t=>{this._access=t.access}),this._ov===null&&!this._loading&&this._load())}async _load(){this._error="";let e=await q(this,{type:"maintenance_supporter/battery_fleet/overview"},{busy:t=>{this._loading=t},onError:t=>{this._error=t}});e!==void 0&&(this._ov=e)}async _act(e,t){this._marking||(this._error="",await q(this,e,{busy:i=>{this._marking=i},reload:async()=>{t?.(),await this._load()},onError:i=>{this._error=i}}))}async _mark(e){await this._act({type:"maintenance_supporter/battery_fleet/mark_replaced",...e?{entity_ids:e}:{}})}async _setExcluded(e,t){await this._act({type:"maintenance_supporter/battery_fleet/set_excluded",entity_id:e,excluded:t})}async _addBattery(e){let t=e.detail?.value;t&&await this._act({type:"maintenance_supporter/battery_fleet/set_included",entity_id:t,included:!0})}async _setTrackSelf(e){await this._setFleetOption("set_track_self_charging",e.target.checked)}async _setDueWithoutSensor(e){await this._setFleetOption("set_due_without_sensor",e.target.checked)}async _setFleetOption(e,t){await this._act({type:`maintenance_supporter/battery_fleet/${e}`,enabled:t})}_rosterOpen(){return Ot(Ee.batteryRosterOpen)!=="0"}_predictedTitle(e,t){if(e.forecast_overdue)return n("battery_fleet_forecast_overdue",t);let i=this._predictedDate(e.days_until??0);if(e.predicted_source==="trend")return n("battery_fleet_predicted_trend",t).replace("{date}",i).replace("{confidence}",n("cal_confidence_"+(e.prediction_confidence||"medium"),t));if(e.lifetime_months!=null&&e.lifetime_source){let a=n("lifetime_source_"+e.lifetime_source,t).replace("{n}",String(e.lifetime_samples??0));return n("battery_fleet_predicted_typical",t).replace("{date}",i).replace("{months}",String(e.lifetime_months)).replace("{type}",e.battery_type).replace("{source}",a)}return n("battery_fleet_predicted_on",t).replace("{date}",i)}_sparkline(e){let t=this._history?.[e.entity_id];if(!t||t.points.length<2)return p;let i=110,a=24,o=2,d=t.points[0][0],h=t.points[t.points.length-1][0],_=Date.now()/1e3,m=e.status!=="low"&&e.predicted_source==="trend"&&e.days_until!=null?_+e.days_until*86400:null,b=Math.max(h,m??h),k=N=>b===d?o:o+(N-d)/(b-d)*(i-2*o),w=N=>o+(1-Math.min(100,Math.max(0,N))/100)*(a-2*o),v=t.points.map(([N,V])=>`${A(k(N))},${A(w(V))}`).join(" "),x=t.points[t.points.length-1][1],T=A(w(t.threshold));return l`<svg
|
||||
`,c([f({attribute:!1})],oe.prototype,"hass",2),c([u()],oe.prototype,"_config",2),c([u()],oe.prototype,"_objects",2),c([u()],oe.prototype,"_loadingObjects",2),c([u()],oe.prototype,"_loadError",2),c([u()],oe.prototype,"_views",2);customElements.get("maintenance-supporter-card-editor")||customElements.define("maintenance-supporter-card-editor",oe);fs();C();async function Mo(r,s,e,t){try{let i=await r.connection.sendMessagePromise({type:"maintenance_supporter/task/undo",entry_id:s,task_id:e});return{ok:!0,message:n(i?.action_ran?"undo_done_action_ran":"undo_done",t)}}catch(i){let a=i?.code;return a==="changed_since"?{ok:!1,message:n("undo_changed",t)}:a==="nothing_to_undo"?{ok:!1,message:n("undo_nothing",t)}:{ok:!1,message:n("action_error",t)}}}function $n(r,s,e,t,i){let a=(o,d)=>r.dispatchEvent(new CustomEvent("hass-notification",{detail:d?{message:o,action:d,duration:1e4}:{message:o},bubbles:!0,composed:!0}));a(n("task_completed_named",t).replace("{task}",e.taskName),{text:n("undo",t),action:()=>{Mo(s,e.entryId,e.taskId,t).then(o=>{a(o.message),o.ok&&i()})}})}R();H();R();H();C();ms();ke();Ze();et();ee();fi();var z=class z extends E{constructor(){super(...arguments);this.flat=!1;this._ov=null;this._loading=!1;this._marking=!1;this._error="";this._history=null;this._rosterSort=z._storedSort();this._typeFilter=null;this._recorded=[];this._access=ue;this._historyRequested=!1;this._localeReady=!1;this._markAll=async()=>{await this._mark(void 0)};this._repair=async()=>{await this._act({type:"maintenance_supporter/battery_fleet/setup",language:this._lang})};this._loadHistory=async e=>{let t=e.target.open;if(Ht(Ee.batteryRosterOpen,t?"1":"0"),!(!t||this._historyRequested)){this._historyRequested=!0;try{let i=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/battery_fleet/overview_history"});this._history=i.series}catch{this._history=null}}}}get _lang(){return P(this.hass)}get _canWrite(){return ae(this.hass?.user,this._access)}connectedCallback(){super.connectedCallback(),this.hass&&this._load()}updated(e){Qt(this,e),e.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,pe(this._lang).then(()=>this.requestUpdate()),B(this.hass).then(t=>{this._access=t.access}),this._ov===null&&!this._loading&&this._load())}async _load(){this._error="";let e=await q(this,{type:"maintenance_supporter/battery_fleet/overview"},{busy:t=>{this._loading=t},onError:t=>{this._error=t}});e!==void 0&&(this._ov=e)}async _act(e,t){this._marking||(this._error="",await q(this,e,{busy:i=>{this._marking=i},reload:async()=>{t?.(),await this._load()},onError:i=>{this._error=i}}))}async _mark(e){await this._act({type:"maintenance_supporter/battery_fleet/mark_replaced",...e?{entity_ids:e}:{}})}async _setExcluded(e,t){await this._act({type:"maintenance_supporter/battery_fleet/set_excluded",entity_id:e,excluded:t})}async _addBattery(e){let t=e.detail?.value;t&&await this._act({type:"maintenance_supporter/battery_fleet/set_included",entity_id:t,included:!0})}async _setTrackSelf(e){await this._setFleetOption("set_track_self_charging",e.target.checked)}async _setDueWithoutSensor(e){await this._setFleetOption("set_due_without_sensor",e.target.checked)}async _setFleetOption(e,t){await this._act({type:`maintenance_supporter/battery_fleet/${e}`,enabled:t})}_rosterOpen(){return Ot(Ee.batteryRosterOpen)!=="0"}_predictedTitle(e,t){if(e.forecast_overdue)return n("battery_fleet_forecast_overdue",t);let i=this._predictedDate(e.days_until??0);if(e.predicted_source==="trend")return n("battery_fleet_predicted_trend",t).replace("{date}",i).replace("{confidence}",n("cal_confidence_"+(e.prediction_confidence||"medium"),t));if(e.lifetime_months!=null&&e.lifetime_source){let a=n("lifetime_source_"+e.lifetime_source,t).replace("{n}",String(e.lifetime_samples??0));return n("battery_fleet_predicted_typical",t).replace("{date}",i).replace("{months}",String(e.lifetime_months)).replace("{type}",e.battery_type).replace("{source}",a)}return n("battery_fleet_predicted_on",t).replace("{date}",i)}_sparkline(e){let t=this._history?.[e.entity_id];if(!t||t.points.length<2)return p;let i=110,a=24,o=2,d=t.points[0][0],h=t.points[t.points.length-1][0],_=Date.now()/1e3,m=e.status!=="low"&&e.predicted_source==="trend"&&e.days_until!=null?_+e.days_until*86400:null,b=Math.max(h,m??h),k=O=>b===d?o:o+(O-d)/(b-d)*(i-2*o),w=O=>o+(1-Math.min(100,Math.max(0,O))/100)*(a-2*o),v=t.points.map(([O,G])=>`${A(k(O))},${A(w(G))}`).join(" "),x=t.points[t.points.length-1][1],T=A(w(t.threshold));return l`<svg
|
||||
class="bf-spark"
|
||||
viewBox="0 0 ${i} ${a}"
|
||||
role="img"
|
||||
@@ -5156,7 +5195,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
x2=${A(k(m))}
|
||||
y2=${T}
|
||||
></line>`:p}
|
||||
</svg>`}static _storedSort(){return Ot(Ee.batteryRosterSort)==="name"?"name":"urgency"}_setSort(e){this._rosterSort=e,jt(Ee.batteryRosterSort,e)}_sortedRoster(e){let t=this._typeFilter===null?e:e.filter(a=>a.battery_type===this._typeFilter);if(this._rosterSort==="name")return t;let i=a=>a.status==="low"?-1e3+(a.level??101)/101:a.days_until??1/0;return[...t].sort((a,o)=>i(a)-i(o)||a.device_name.localeCompare(o.device_name))}_predictedDate(e){return H(Ar(We(),e),this._lang)}_fmtDate(e){return Number.isFinite(e)?H(new Date(e).toISOString(),this._lang):"\u2014"}_shoppingLine(e){return Object.entries(e).map(([t,i])=>l`<button
|
||||
</svg>`}static _storedSort(){return Ot(Ee.batteryRosterSort)==="name"?"name":"urgency"}_setSort(e){this._rosterSort=e,Ht(Ee.batteryRosterSort,e)}_sortedRoster(e){let t=this._typeFilter===null?e:e.filter(a=>a.battery_type===this._typeFilter);if(this._rosterSort==="name")return t;let i=a=>a.status==="low"?-1e3+(a.level??101)/101:a.days_until??1/0;return[...t].sort((a,o)=>i(a)-i(o)||a.device_name.localeCompare(o.device_name))}_predictedDate(e){return j(Cr(We(),e),this._lang)}_fmtDate(e){return Number.isFinite(e)?j(new Date(e).toISOString(),this._lang):"\u2014"}_shoppingLine(e){return Object.entries(e).map(([t,i])=>l`<button
|
||||
class="bf-type-chip ${this._typeFilter===t?"bf-type-chip-active":""}"
|
||||
title=${n("battery_fleet_filter_type",this._lang)}
|
||||
@click=${()=>this._toggleTypeFilter(t)}
|
||||
@@ -5797,7 +5836,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
accent-color: var(--primary-color);
|
||||
margin: 0;
|
||||
}
|
||||
`,c([f({attribute:!1})],z.prototype,"hass",2),c([f({type:Boolean})],z.prototype,"flat",2),c([u()],z.prototype,"_ov",2),c([u()],z.prototype,"_loading",2),c([u()],z.prototype,"_marking",2),c([u()],z.prototype,"_error",2),c([u()],z.prototype,"_history",2),c([u()],z.prototype,"_rosterSort",2),c([u()],z.prototype,"_typeFilter",2),c([u()],z.prototype,"_recorded",2),c([u()],z.prototype,"_access",2);var fs=z;customElements.get("maintenance-battery-fleet-section")||customElements.define("maintenance-battery-fleet-section",fs);var at=class extends E{constructor(){super(...arguments);this._config={type:"custom:maintenance-battery-fleet-card"}}static getStubConfig(){return{type:"custom:maintenance-battery-fleet-card"}}setConfig(e){this._config=e}getCardSize(){return 6}render(){return this.hass?l`
|
||||
`,c([f({attribute:!1})],z.prototype,"hass",2),c([f({type:Boolean})],z.prototype,"flat",2),c([u()],z.prototype,"_ov",2),c([u()],z.prototype,"_loading",2),c([u()],z.prototype,"_marking",2),c([u()],z.prototype,"_error",2),c([u()],z.prototype,"_history",2),c([u()],z.prototype,"_rosterSort",2),c([u()],z.prototype,"_typeFilter",2),c([u()],z.prototype,"_recorded",2),c([u()],z.prototype,"_access",2);var vs=z;customElements.get("maintenance-battery-fleet-section")||customElements.define("maintenance-battery-fleet-section",vs);var at=class extends E{constructor(){super(...arguments);this._config={type:"custom:maintenance-battery-fleet-card"}}static getStubConfig(){return{type:"custom:maintenance-battery-fleet-card"}}setConfig(e){this._config=e}getCardSize(){return 6}render(){return this.hass?l`
|
||||
<ha-card .header=${this._config.title||void 0}>
|
||||
<div class="content">
|
||||
<maintenance-battery-fleet-section flat .hass=${this.hass}></maintenance-battery-fleet-section>
|
||||
@@ -5810,17 +5849,17 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
.content {
|
||||
padding: 12px 16px 14px;
|
||||
}
|
||||
`,c([f({attribute:!1})],at.prototype,"hass",2),c([u()],at.prototype,"_config",2);customElements.get("maintenance-battery-fleet-card")||customElements.define("maintenance-battery-fleet-card",at);Ye({type:"maintenance-battery-fleet-card",name:"Battery Fleet",description:"All tracked batteries: what is low now, what runs out soon, and what to buy.",preview:!1});le();R();j();var Is=["today","dashboard","calendar","settings"];C();var dt="maintenance-supporter-panel-card",Ps="maintenance-supporter-panel",Ns="maintenance-supporter",Ls=870,El=320,Sl=480;function Tl(r){let e=r?.panels?.[Ns]?.config?._panel_custom?.module_url;return typeof e=="string"&&e.startsWith("/")?e:null}function Al(r){if(r==null||r===""||r==="fill")return null;if(typeof r=="number")return r>0?`${Math.round(r)}px`:null;let s=String(r).trim();return/^\d+(\.\d+)?$/.test(s)?`${s}px`:s||null}var Os=class extends HTMLElement{constructor(){super();this._config={type:`custom:${dt}`};this._panel=null;this._loading=null;this._observer=null;this._lastWidth=-1;this._preview=!1;this._onResize=()=>this._layout(!0);let e=this.attachShadow({mode:"open"}),t=document.createElement("style");t.textContent=`
|
||||
`,c([f({attribute:!1})],at.prototype,"hass",2),c([u()],at.prototype,"_config",2);customElements.get("maintenance-battery-fleet-card")||customElements.define("maintenance-battery-fleet-card",at);Ye({type:"maintenance-battery-fleet-card",name:"Battery Fleet",description:"All tracked batteries: what is low now, what runs out soon, and what to buy.",preview:!1});le();R();H();var Ps=["today","dashboard","calendar","settings"];C();var dt="maintenance-supporter-panel-card",Ls="maintenance-supporter-panel",Os="maintenance-supporter",Ns=870,Cl=320,Rl=480;function Il(r){let e=r?.panels?.[Os]?.config?._panel_custom?.module_url;return typeof e=="string"&&e.startsWith("/")?e:null}function Pl(r){if(r==null||r===""||r==="fill")return null;if(typeof r=="number")return r>0?`${Math.round(r)}px`:null;let s=String(r).trim();return/^\d+(\.\d+)?$/.test(s)?`${s}px`:s||null}var Hs=class extends HTMLElement{constructor(){super();this._config={type:`custom:${dt}`};this._panel=null;this._loading=null;this._observer=null;this._lastWidth=-1;this._preview=!1;this._onResize=()=>this._layout(!0);let e=this.attachShadow({mode:"open"}),t=document.createElement("style");t.textContent=`
|
||||
:host { display: block; box-sizing: border-box; overflow: hidden; border-radius: var(--ha-card-border-radius, 12px); }
|
||||
.status { padding: 16px; color: var(--secondary-text-color); font-size: 14px; }
|
||||
${Ps} { display: block; height: 100%; }
|
||||
`,e.appendChild(t),this._status=document.createElement("div"),this._status.className="status",this._status.hidden=!0,e.appendChild(this._status)}static getConfigElement(){return document.createElement("maintenance-supporter-panel-card-editor")}static getStubConfig(){return{type:`custom:${dt}`}}set preview(e){this._preview=!!e,this._layout(!0)}get preview(){return this._preview}_inEditorPreview(){if(this._preview)return!0;let e=this;for(let t=0;e&&t<40;t++){let i=e;if(i.tagName==="HUI-CARD-PREVIEW"||i.tagName==="HUI-DIALOG-EDIT-CARD")return!0;e=i.parentElement??i.getRootNode().host??null}return!1}setConfig(e){this._config={...e},this._panel&&(this._panel.presets=this._presets()),this._layout(!0)}getCardSize(){return 12}getGridOptions(){return{columns:"full",rows:"auto"}}set hass(e){this._hass=e,this._panel?this._panel.hass=e:this._mount()}get hass(){return this._hass}connectedCallback(){typeof ResizeObserver<"u"&&!this._observer&&(this._observer=new ResizeObserver(()=>this._layout(!1)),this._observer.observe(this)),window.addEventListener("resize",this._onResize),this._layout(!0),this._hass&&this._mount()}disconnectedCallback(){this._observer?.disconnect(),this._observer=null,window.removeEventListener("resize",this._onResize)}_presets(){let e={};return Is.includes(this._config.tab??"")&&(e.tab=this._config.tab),typeof this._config.view=="string"&&this._config.view.trim()&&(e.view=this._config.view.trim()),e}_setStatus(e){this._status.textContent=e,this._status.hidden=!1}async _mount(){if(this._panel||this._loading||!this._hass||!this.isConnected)return;let e=P(this._hass);if(!customElements.get(Ps)){let i=Tl(this._hass);if(!i){this._setStatus(n("panel_card_not_registered",e));return}this._loading=import(i).then(()=>{},()=>{throw this._setStatus(n("panel_card_load_failed",e)),new Error("panel bundle failed to load")});try{await this._loading}catch{return}finally{this._loading=null}if(this._panel||!this._hass||!this.isConnected)return}let t=document.createElement(Ps);t.embedded=!0,t.panel=this._hass.panels?.[Ns]??{url_path:Ns},t.presets=this._presets(),t.narrow=this._narrow(),t.hass=this._hass,this._status.hidden=!0,this.shadowRoot.appendChild(t),this._panel=t,this._layout(!0)}_narrow(){let e=this.getBoundingClientRect().width;return e>0?e<Ls:window.innerWidth<Ls}_layout(e){let t=this.getBoundingClientRect(),i=Math.round(t.width)!==this._lastWidth;if(!e&&!i)return;this._lastWidth=Math.round(t.width),this._panel&&t.width>0&&(this._panel.narrow=t.width<Ls);let a=Al(this._config.height);if(a){this.style.height=a;return}if(!this.isConnected)return;if(this._inEditorPreview()){this.style.height=`${Sl}px`;return}let o=Math.max(0,this._unscrolledTop(t)),d=Math.max(El,Math.floor(window.innerHeight-o));this.style.height=`${d}px`}_unscrolledTop(e){let t=e.top+window.scrollY,i=this.ownerDocument,a=this.parentElement??this.getRootNode().host??null;for(let o=0;a&&o<100;o++){let d=a;d!==i.documentElement&&d!==i.body&&typeof d.scrollTop=="number"&&d.scrollTop>0&&(t+=d.scrollTop),a=d.parentElement??d.getRootNode().host??null}return t}},De=class extends E{constructor(){super(...arguments);this._config={type:`custom:${dt}`};this._views=[];this._viewsLoaded=!1}setConfig(e){this._config={...e}}updated(e){super.updated(e),e.has("hass")&&this.hass&&!this._viewsLoaded&&(this._viewsLoaded=!0,this._loadViews())}async _loadViews(){try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/views/list"});this._views=e?.views||[]}catch{this._views=[]}}_set(e,t){let i={...this._config};t===""||t===void 0?delete i[e]:i[e]=t,this._config=i,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:i},bubbles:!0,composed:!0}))}render(){let e=P(this.hass),t=i=>n(i==="today"?"tab_today":i==="calendar"?"tab_calendar":i,e);return l`
|
||||
${Ls} { display: block; height: 100%; }
|
||||
`,e.appendChild(t),this._status=document.createElement("div"),this._status.className="status",this._status.hidden=!0,e.appendChild(this._status)}static getConfigElement(){return document.createElement("maintenance-supporter-panel-card-editor")}static getStubConfig(){return{type:`custom:${dt}`}}set preview(e){this._preview=!!e,this._layout(!0)}get preview(){return this._preview}_inEditorPreview(){if(this._preview)return!0;let e=this;for(let t=0;e&&t<40;t++){let i=e;if(i.tagName==="HUI-CARD-PREVIEW"||i.tagName==="HUI-DIALOG-EDIT-CARD")return!0;e=i.parentElement??i.getRootNode().host??null}return!1}setConfig(e){this._config={...e},this._panel&&(this._panel.presets=this._presets()),this._layout(!0)}getCardSize(){return 12}getGridOptions(){return{columns:"full",rows:"auto"}}set hass(e){this._hass=e,this._panel?this._panel.hass=e:this._mount()}get hass(){return this._hass}connectedCallback(){typeof ResizeObserver<"u"&&!this._observer&&(this._observer=new ResizeObserver(()=>this._layout(!1)),this._observer.observe(this)),window.addEventListener("resize",this._onResize),this._layout(!0),this._hass&&this._mount()}disconnectedCallback(){this._observer?.disconnect(),this._observer=null,window.removeEventListener("resize",this._onResize)}_presets(){let e={};return Ps.includes(this._config.tab??"")&&(e.tab=this._config.tab),typeof this._config.view=="string"&&this._config.view.trim()&&(e.view=this._config.view.trim()),e}_setStatus(e){this._status.textContent=e,this._status.hidden=!1}async _mount(){if(this._panel||this._loading||!this._hass||!this.isConnected)return;let e=P(this._hass);if(!customElements.get(Ls)){let i=Il(this._hass);if(!i){this._setStatus(n("panel_card_not_registered",e));return}this._loading=import(i).then(()=>{},()=>{throw this._setStatus(n("panel_card_load_failed",e)),new Error("panel bundle failed to load")});try{await this._loading}catch{return}finally{this._loading=null}if(this._panel||!this._hass||!this.isConnected)return}let t=document.createElement(Ls);t.embedded=!0,t.panel=this._hass.panels?.[Os]??{url_path:Os},t.presets=this._presets(),t.narrow=this._narrow(),t.hass=this._hass,this._status.hidden=!0,this.shadowRoot.appendChild(t),this._panel=t,this._layout(!0)}_narrow(){let e=this.getBoundingClientRect().width;return e>0?e<Ns:window.innerWidth<Ns}_layout(e){let t=this.getBoundingClientRect(),i=Math.round(t.width)!==this._lastWidth;if(!e&&!i)return;this._lastWidth=Math.round(t.width),this._panel&&t.width>0&&(this._panel.narrow=t.width<Ns);let a=Pl(this._config.height);if(a){this.style.height=a;return}if(!this.isConnected)return;if(this._inEditorPreview()){this.style.height=`${Rl}px`;return}let o=Math.max(0,this._unscrolledTop(t)),d=Math.max(Cl,Math.floor(window.innerHeight-o));this.style.height=`${d}px`}_unscrolledTop(e){let t=e.top+window.scrollY,i=this.ownerDocument,a=this.parentElement??this.getRootNode().host??null;for(let o=0;a&&o<100;o++){let d=a;d!==i.documentElement&&d!==i.body&&typeof d.scrollTop=="number"&&d.scrollTop>0&&(t+=d.scrollTop),a=d.parentElement??d.getRootNode().host??null}return t}},De=class extends E{constructor(){super(...arguments);this._config={type:`custom:${dt}`};this._views=[];this._viewsLoaded=!1}setConfig(e){this._config={...e}}updated(e){super.updated(e),e.has("hass")&&this.hass&&!this._viewsLoaded&&(this._viewsLoaded=!0,this._loadViews())}async _loadViews(){try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/views/list"});this._views=e?.views||[]}catch{this._views=[]}}_set(e,t){let i={...this._config};t===""||t===void 0?delete i[e]:i[e]=t,this._config=i,this.dispatchEvent(new CustomEvent("config-changed",{detail:{config:i},bubbles:!0,composed:!0}))}render(){let e=P(this.hass),t=i=>n(i==="today"?"tab_today":i==="calendar"?"tab_calendar":i,e);return l`
|
||||
<div class="editor">
|
||||
<div class="field">
|
||||
<div class="field-label">${n("panel_card_tab",e)}</div>
|
||||
<select class="tab-select" .value=${this._config.tab||""} @change=${i=>this._set("tab",i.target.value)}>
|
||||
<option value="" ?selected=${!this._config.tab}>${n("panel_card_tab_default",e)}</option>
|
||||
${Is.map(i=>l`<option value=${i} ?selected=${this._config.tab===i}>${t(i)}</option>`)}
|
||||
${Ps.map(i=>l`<option value=${i} ?selected=${this._config.tab===i}>${t(i)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
${this._views.length>0?l`<div class="field">
|
||||
@@ -5845,8 +5884,8 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
.field-label { font-size: 13px; color: var(--secondary-text-color); font-weight: 500; }
|
||||
.field-help { font-size: 12px; color: var(--secondary-text-color); }
|
||||
select { padding: 8px; border-radius: 4px; border: 1px solid var(--divider-color); background: var(--card-background-color); color: var(--primary-text-color); font-size: 14px; max-width: 320px; }
|
||||
`,c([f({attribute:!1})],De.prototype,"hass",2),c([u()],De.prototype,"_config",2),c([u()],De.prototype,"_views",2);customElements.get(dt)||customElements.define(dt,Os);customElements.get("maintenance-supporter-panel-card-editor")||customElements.define("maintenance-supporter-panel-card-editor",De);Ye({type:dt,name:"Maintenance Supporter \u2014 Panel",description:"The complete Maintenance Supporter panel as a card \u2014 for a panel view / dashboard subview without the sidebar entry.",preview:!1});si();var va=2,F=class extends E{constructor(){super(...arguments);this._config={type:"custom:maintenance-supporter-card"};this._globalRowStyle="buttons_compact";this._settings=Xe;this._firstLoadDone=!1;this._objects=[];this._stats=null;this._unsub=null;this._viewFilters=null;this._userNames={};this._userPersons={};this._userService=null;this._userNamesLoaded=!1;this._taskDocs={};this._docsLoadedFor=new Set;this._dataLoaded=!1;this._lastConnection=null;this._onCompleted=async e=>{await this._loadData();let t=e.detail;t?.undo&&kn(this,this.hass,t,this._lang,()=>{this._loadData()})}}get _lang(){return P(this.hass)}static getConfigElement(){return document.createElement("maintenance-supporter-card-editor")}static getStubConfig(){return{type:"custom:maintenance-supporter-card",show_header:!0,show_actions:!0,filter_status:[...Di],max_items:10}}setConfig(e){let t=e.view_id!==this._config.view_id;this._config=e,t&&this._dataLoaded&&this.hass&&this._loadViewFilters()}getCardSize(){return 3}connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),this._unsub&&(this._unsub(),this._unsub=null),this._dataLoaded=!1,this._lastConnection=null}updated(e){if(super.updated(e),Yt(this,e),this.hass&&!this._userNamesLoaded&&this._config.show_assignee!==!1&&this._objects.some(t=>t.tasks.some(i=>i.responsible_user_id))&&this._loadUserNames(),this.hass&&this._config.show_documents!==!1)for(let t of this._objects)this._docsLoadedFor.has(t.entry_id)||t.tasks.some(i=>(i.document_count??0)>0)&&this._loadDocuments(t.entry_id);if(e.has("hass")&&this.hass){if(!this._dataLoaded)this._dataLoaded=!0,this._lastConnection=this.hass.connection,this._loadData(),this._subscribe();else if(this.hass.connection!==this._lastConnection){if(this._lastConnection=this.hass.connection,this._unsub){try{this._unsub()}catch{}this._unsub=null}this._subscribe(),this._loadData()}}}async _loadData(){K(this.hass).then(t=>{this._settings=t,this._globalRowStyle=t.rowActionStyle}).catch(()=>{});let e=this._loadViewFilters();try{let[t,i]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects",compact:!0}),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/statistics"})]);await e,this._objects=yt(t.objects),this._stats=i,Qt(this._stats.budget)}catch{}await e,this._firstLoadDone=!0}_assigneeName(e){if(this._config.show_assignee===!1)return"";let t=e.responsible_user_id;return t&&this._userNames[t]||""}async _loadUserNames(){this._userNamesLoaded=!0,this._userService?this._userService.updateHass(this.hass):this._userService=new Qe(this.hass);try{let e=await this._userService.getUsers();this._userPersons=Object.fromEntries(e.map(t=>[t.id,ri(t)])),this._userNames=Object.fromEntries(e.map(t=>[t.id,t.name]))}catch{}}async _loadDocuments(e){this._docsLoadedFor.add(e);try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/documents/list",entry_id:e}),i={};for(let a of t.documents||[])if(!qr(a))for(let o of a.task_ids||[])(i[o]||=[]).push({id:a.id,title:a.title,kind:a.kind,url:a.url});this._taskDocs={...this._taskDocs,[e]:i}}catch{}}_docsFor(e,t){if(this._config.show_documents===!1)return[];let a=[...this._taskDocs[e]?.[t.id]||[]];return t.documentation_url&&a.push({id:`url:${t.id}`,title:n("documentation_label",this._lang),kind:"weblink",url:t.documentation_url}),a}_renderDocChips(e,t){let i=this._docsFor(e,t);if(!i.length)return p;let a=i.slice(va);return l`<div class="doc-chips">
|
||||
${i.slice(0,va).map(o=>l`
|
||||
`,c([f({attribute:!1})],De.prototype,"hass",2),c([u()],De.prototype,"_config",2),c([u()],De.prototype,"_views",2);customElements.get(dt)||customElements.define(dt,Hs);customElements.get("maintenance-supporter-panel-card-editor")||customElements.define("maintenance-supporter-panel-card-editor",De);Ye({type:dt,name:"Maintenance Supporter \u2014 Panel",description:"The complete Maintenance Supporter panel as a card \u2014 for a panel view / dashboard subview without the sidebar entry.",preview:!1});ri();var wa=2,F=class extends E{constructor(){super(...arguments);this._config={type:"custom:maintenance-supporter-card"};this._globalRowStyle="buttons_compact";this._settings=Xe;this._firstLoadDone=!1;this._objects=[];this._stats=null;this._unsub=null;this._viewFilters=null;this._userNames={};this._userPersons={};this._userService=null;this._userNamesLoaded=!1;this._taskDocs={};this._docsLoadedFor=new Set;this._dataLoaded=!1;this._lastConnection=null;this._onCompleted=async e=>{await this._loadData();let t=e.detail;t?.undo&&$n(this,this.hass,t,this._lang,()=>{this._loadData()})}}get _lang(){return P(this.hass)}static getConfigElement(){return document.createElement("maintenance-supporter-card-editor")}static getStubConfig(){return{type:"custom:maintenance-supporter-card",show_header:!0,show_actions:!0,filter_status:[...Mi],max_items:10}}setConfig(e){let t=e.view_id!==this._config.view_id;this._config=e,t&&this._dataLoaded&&this.hass&&this._loadViewFilters()}getCardSize(){return 3}connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),this._unsub&&(this._unsub(),this._unsub=null),this._dataLoaded=!1,this._lastConnection=null}updated(e){if(super.updated(e),Qt(this,e),this.hass&&!this._userNamesLoaded&&this._config.show_assignee!==!1&&this._objects.some(t=>t.tasks.some(i=>i.responsible_user_id))&&this._loadUserNames(),this.hass&&this._config.show_documents!==!1)for(let t of this._objects)this._docsLoadedFor.has(t.entry_id)||t.tasks.some(i=>(i.document_count??0)>0)&&this._loadDocuments(t.entry_id);if(e.has("hass")&&this.hass){if(!this._dataLoaded)this._dataLoaded=!0,this._lastConnection=this.hass.connection,this._loadData(),this._subscribe();else if(this.hass.connection!==this._lastConnection){if(this._lastConnection=this.hass.connection,this._unsub){try{this._unsub()}catch{}this._unsub=null}this._subscribe(),this._loadData()}}}async _loadData(){B(this.hass).then(t=>{this._settings=t,this._globalRowStyle=t.rowActionStyle}).catch(()=>{});let e=this._loadViewFilters();try{let[t,i]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects",compact:!0}),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/statistics"})]);await e,this._objects=yt(t.objects),this._stats=i,Zt(this._stats.budget)}catch{}await e,this._firstLoadDone=!0}_assigneeName(e){if(this._config.show_assignee===!1)return"";let t=e.responsible_user_id;return t&&this._userNames[t]||""}async _loadUserNames(){this._userNamesLoaded=!0,this._userService?this._userService.updateHass(this.hass):this._userService=new Qe(this.hass);try{let e=await this._userService.getUsers();this._userPersons=Object.fromEntries(e.map(t=>[t.id,ni(t)])),this._userNames=Object.fromEntries(e.map(t=>[t.id,t.name]))}catch{}}async _loadDocuments(e){this._docsLoadedFor.add(e);try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/documents/list",entry_id:e}),i={};for(let a of t.documents||[])if(!Dr(a))for(let o of a.task_ids||[])(i[o]||=[]).push({id:a.id,title:a.title,kind:a.kind,url:a.url});this._taskDocs={...this._taskDocs,[e]:i}}catch{}}_docsFor(e,t){if(this._config.show_documents===!1)return[];let a=[...this._taskDocs[e]?.[t.id]||[]];return t.documentation_url&&a.push({id:`url:${t.id}`,title:n("documentation_label",this._lang),kind:"weblink",url:t.documentation_url}),a}_renderDocChips(e,t){let i=this._docsFor(e,t);if(!i.length)return p;let a=i.slice(wa);return l`<div class="doc-chips">
|
||||
${i.slice(0,wa).map(o=>l`
|
||||
<button
|
||||
type="button"
|
||||
class="doc-chip"
|
||||
@@ -5859,7 +5898,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
`)}
|
||||
${a.length?l`<span class="doc-chip doc-more" title="${a.map(o=>o.title).join(`
|
||||
`)}">+${a.length}</span>`:p}
|
||||
</div>`}async _openDoc(e){if(e.kind==="weblink"&&e.url){ii(e.url)&&window.open(e.url,"_blank","noopener");return}try{await Hr(this.hass,e.id)}catch{}}async _loadViewFilters(){if(!this._config.view_id){this._viewFilters=null;return}try{let t=((await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/views/list"})).views||[]).find(i=>i.id===this._config.view_id);this._viewFilters=t?t.filters:null}catch{this._viewFilters=null}}async _subscribe(){try{let e=await this.hass.connection.subscribeMessage(t=>{let i=sr(this._objects,t);i!==null&&(this._objects=i)},{type:"maintenance_supporter/subscribe",deltas:!0,compact:!0});if(!this.isConnected){e();return}this._unsub=e}catch{}}get _selectedTasks(){let e=[],{filter_status:t,filter_objects:i,entity_ids:a,filter_due_min_days:o,filter_due_max_days:d,filter_labels:h,filter_priority:_,filter_areas:m}=this._config,b=a?.length?new Set(a):null,k=o!==void 0||d!==void 0,w=this._viewFilters,v=w?.user_id==="current_user"?this.hass.user?.id??null:w?.user_id??null;for(let x of this._objects)if(!(i?.length&&!i.includes(x.object.name))){if(m?.length){let T=x.object.area_id;if(!T||!m.includes(T))continue}for(let T of x.tasks)if(!T.is_done&&!(T.archived||x.object.archived)&&!(t?.length&&!t.includes(T.status))&&!(h?.length&&!(T.labels||[]).some(N=>h.includes(N)))&&!(_?.length&&!_.includes(T.priority||"normal"))&&!(b&&!(T.sensor_entity_id&&b.has(T.sensor_entity_id)||T.binary_sensor_entity_id&&b.has(T.binary_sensor_entity_id)))){if(k){let N=T.days_until_due;if(N==null||o!==void 0&&N<o||d!==void 0&&N>d)continue}w&&(w.status&&T.status!==w.status||w.label&&!(T.labels||[]).includes(w.label)||w.priority&&(T.priority||"normal")!==w.priority||v&&T.responsible_user_id!==v)||e.push({entry_id:x.entry_id,object_name:x.object.name,task:T})}}return e.sort((x,T)=>{let N=Mi(x.task.status)-Mi(T.task.status);return N!==0?N:(x.task.days_until_due??1/0)-(T.task.days_until_due??1/0)}),e}_openComplete(e,t){let i=this.shadowRoot.querySelector("maintenance-complete-dialog");li(i,oi({entryId:e,taskId:t.id,taskName:t.name,task:t,objects:this._objects,lang:this._lang,features:this._settings.features,currencySymbol:Ne(this._stats?.budget),partsCostMode:this._settings.partsCostMode}),this._lang)}_headerCounts(e){let t={overdue:0,due_soon:0,triggered:0};for(let{task:i}of e)_r(i.status)&&(t[i.status]+=1);return t}_openTaskDetail(e,t){Rs(e,t)}render(){let e=this._lang,t=this._config.title||n("maintenance",e),i=this._config.show_header!==!1,a=(this._config.action_style??(this._globalRowStyle==="icons"?"icons":"buttons"))==="buttons",o=this._config.show_actions!==!1,d=o&&ae(this.hass?.user,this._settings.access),h=this._config.compact||!1,_=this._selectedTasks,{max_items:m}=this._config,b=m&&m>0?_.slice(0,m):_,k=this._objects.length||this._stats?this._headerCounts(_):null;return l`
|
||||
</div>`}async _openDoc(e){if(e.kind==="weblink"&&e.url){si(e.url)&&window.open(e.url,"_blank","noopener");return}try{await qr(this.hass,e.id)}catch{}}async _loadViewFilters(){if(!this._config.view_id){this._viewFilters=null;return}try{let t=((await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/views/list"})).views||[]).find(i=>i.id===this._config.view_id);this._viewFilters=t?t.filters:null}catch{this._viewFilters=null}}async _subscribe(){try{let e=await this.hass.connection.subscribeMessage(t=>{let i=rr(this._objects,t);i!==null&&(this._objects=i)},{type:"maintenance_supporter/subscribe",deltas:!0,compact:!0});if(!this.isConnected){e();return}this._unsub=e}catch{}}get _selectedTasks(){let e=[],{filter_status:t,filter_objects:i,entity_ids:a,filter_due_min_days:o,filter_due_max_days:d,filter_labels:h,filter_priority:_,filter_areas:m}=this._config,b=a?.length?new Set(a):null,k=o!==void 0||d!==void 0,w=this._viewFilters,v=w?.user_id==="current_user"?this.hass.user?.id??null:w?.user_id??null;for(let x of this._objects)if(!(i?.length&&!i.includes(x.object.name))){if(m?.length){let T=x.object.area_id;if(!T||!m.includes(T))continue}for(let T of x.tasks)if(!T.is_done&&!(T.archived||x.object.archived)&&!(t?.length&&!t.includes(T.status))&&!(h?.length&&!(T.labels||[]).some(O=>h.includes(O)))&&!(_?.length&&!_.includes(T.priority||"normal"))&&!(b&&!(T.sensor_entity_id&&b.has(T.sensor_entity_id)||T.binary_sensor_entity_id&&b.has(T.binary_sensor_entity_id)))){if(k){let O=T.days_until_due;if(O==null||o!==void 0&&O<o||d!==void 0&&O>d)continue}w&&(w.status&&T.status!==w.status||w.label&&!(T.labels||[]).includes(w.label)||w.priority&&(T.priority||"normal")!==w.priority||v&&T.responsible_user_id!==v)||e.push({entry_id:x.entry_id,object_name:x.object.name,task:T})}}return e.sort((x,T)=>{let O=zi(x.task.status)-zi(T.task.status);return O!==0?O:(x.task.days_until_due??1/0)-(T.task.days_until_due??1/0)}),e}_openComplete(e,t){let i=this.shadowRoot.querySelector("maintenance-complete-dialog");ci(i,li({entryId:e,taskId:t.id,taskName:t.name,task:t,objects:this._objects,lang:this._lang,features:this._settings.features,currencySymbol:Ne(this._stats?.budget),partsCostMode:this._settings.partsCostMode}),this._lang)}_headerCounts(e){let t={overdue:0,due_soon:0,triggered:0};for(let{task:i}of e)mr(i.status)&&(t[i.status]+=1);return t}_openTaskDetail(e,t){Is(e,t)}render(){let e=this._lang,t=this._config.title||n("maintenance",e),i=this._config.show_header!==!1,a=(this._config.action_style??(this._globalRowStyle==="icons"?"icons":"buttons"))==="buttons",o=this._config.show_actions!==!1,d=o&&ae(this.hass?.user,this._settings.access),h=this._config.compact||!1,_=this._selectedTasks,{max_items:m}=this._config,b=m&&m>0?_.slice(0,m):_,k=this._objects.length||this._stats?this._headerCounts(_):null;return l`
|
||||
<ha-card>
|
||||
<div class="card-header">
|
||||
<h1>${t}</h1>
|
||||
@@ -5875,14 +5914,14 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
<ha-icon-button
|
||||
class="hdr-add"
|
||||
title="${n("new_object",e)}"
|
||||
@click=${()=>As()}
|
||||
@click=${()=>Cs()}
|
||||
>
|
||||
<ha-icon icon="mdi:plus-box"></ha-icon>
|
||||
</ha-icon-button>
|
||||
<ha-icon-button
|
||||
class="hdr-add"
|
||||
title="${n("add_task",e)}"
|
||||
@click=${()=>Cs("",this._objects)}
|
||||
@click=${()=>Rs("",this._objects)}
|
||||
>
|
||||
<ha-icon icon="mdi:playlist-plus"></ha-icon>
|
||||
</ha-icon-button>
|
||||
@@ -5905,7 +5944,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
<div class="status-dot" style="background: ${xt[x.status]||"#ccc"}"></div>
|
||||
<div class="task-info">
|
||||
<div class="task-name">
|
||||
${x.name}${Fr(x.next_event_titles)}
|
||||
${x.name}${Ur(x.next_event_titles)}
|
||||
${x.due_override?l`<ha-icon
|
||||
class="postponed-icon"
|
||||
icon="mdi:calendar-clock"
|
||||
@@ -5913,16 +5952,16 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
></ha-icon>`:p}
|
||||
</div>
|
||||
${h?this._assigneeName(x)?l`<div class="task-meta compact-assignee" title="${this._assigneeName(x)}">
|
||||
${is(this._userPersons[x.responsible_user_id])}${this._assigneeName(x)}
|
||||
${ss(this._userPersons[x.responsible_user_id])}${this._assigneeName(x)}
|
||||
</div>`:p:l`<div class="task-meta">
|
||||
${v} · ${n(x.type,e)}${ye(x)?l` · ${ye(x)}`:p}${this._assigneeName(x)?l` · <span class="assignee"
|
||||
>${is(this._userPersons[x.responsible_user_id])}${this._assigneeName(x)}</span
|
||||
>${ss(this._userPersons[x.responsible_user_id])}${this._assigneeName(x)}</span
|
||||
>`:p}
|
||||
</div>`}
|
||||
</div>
|
||||
${this._renderDocChips(w,x)}
|
||||
<div class="task-due">
|
||||
${x.days_until_due!==null&&x.days_until_due!==void 0?x.days_until_due<0?l`<span class="overdue-text">${Qi(x.days_until_due,e)}</span>`:Qi(x.days_until_due,e):x.trigger_active?"\u26A1":"\u2014"}
|
||||
${x.days_until_due!==null&&x.days_until_due!==void 0?x.days_until_due<0?l`<span class="overdue-text">${Zi(x.days_until_due,e)}</span>`:Zi(x.days_until_due,e):x.trigger_active?"\u26A1":"\u2014"}
|
||||
</div>
|
||||
${o&&a?l`
|
||||
<ha-button
|
||||
@@ -5953,7 +5992,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
.hass=${this.hass}
|
||||
@task-completed=${this._onCompleted}
|
||||
></maintenance-complete-dialog>
|
||||
`}};F.styles=[ti,$`
|
||||
`}};F.styles=[ii,$`
|
||||
ha-card { overflow: hidden; }
|
||||
|
||||
.card-header {
|
||||
@@ -6120,7 +6159,7 @@ ${h?`<div class="sub">${h}</div>`:""}
|
||||
/* DESIGN PROTOTYPE (#145): action_style: buttons — HA-native labelled button. */
|
||||
.complete-btn-text { --ha-button-font-size: 13px; flex: none; white-space: nowrap; }
|
||||
.complete-btn-text ha-icon { --mdc-icon-size: 18px; }
|
||||
`],c([f({attribute:!1})],F.prototype,"hass",2),c([u()],F.prototype,"_config",2),c([u()],F.prototype,"_globalRowStyle",2),c([u()],F.prototype,"_settings",2),c([u()],F.prototype,"_firstLoadDone",2),c([u()],F.prototype,"_objects",2),c([u()],F.prototype,"_stats",2),c([u()],F.prototype,"_unsub",2),c([u()],F.prototype,"_viewFilters",2),c([u()],F.prototype,"_userNames",2),c([u()],F.prototype,"_taskDocs",2);customElements.get("maintenance-supporter-card")||customElements.define("maintenance-supporter-card",F);Ye({type:"maintenance-supporter-card",name:"Maintenance Supporter",description:"Overview of your maintenance tasks with quick actions.",preview:!0});si();Mr(["maintenance-supporter-card","maintenance-battery-fleet-card","maintenance-supporter-panel-card"],import.meta.url);export{F as MaintenanceSupporterCard};
|
||||
`],c([f({attribute:!1})],F.prototype,"hass",2),c([u()],F.prototype,"_config",2),c([u()],F.prototype,"_globalRowStyle",2),c([u()],F.prototype,"_settings",2),c([u()],F.prototype,"_firstLoadDone",2),c([u()],F.prototype,"_objects",2),c([u()],F.prototype,"_stats",2),c([u()],F.prototype,"_unsub",2),c([u()],F.prototype,"_viewFilters",2),c([u()],F.prototype,"_userNames",2),c([u()],F.prototype,"_taskDocs",2);customElements.get("maintenance-supporter-card")||customElements.define("maintenance-supporter-card",F);Ye({type:"maintenance-supporter-card",name:"Maintenance Supporter",description:"Overview of your maintenance tasks with quick actions.",preview:!0});ri();zr(["maintenance-supporter-card","maintenance-battery-fleet-card","maintenance-supporter-panel-card"],import.meta.url);export{F as MaintenanceSupporterCard};
|
||||
/*! Bundled license information:
|
||||
|
||||
@lit/reactive-element/css-tag.js:
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,2 +1,2 @@
|
||||
/*! maintenance_supporter frontend 2.98.0 */
|
||||
var v="2.98.0";var f="maintenance-supporter",L=`ll-strategy-dashboard-${f}`,C="hui-maintenance-supporter-strategy-editor",M=["maintenance-supporter-section","maintenance-supporter-vacation","maintenance-supporter-budget","maintenance-supporter-groups"],D=`/maintenance_supporter_strategy/maintenance-dashboard-strategy.js?v=${v}`,w=null;function h(){return w||(w=import(D)),w}async function I(){let e=await h();if(!e.MaintenanceDashboardStrategy)throw new Error("[maintenance-supporter] strategy bundle loaded but did not export MaintenanceDashboardStrategy");return e.MaintenanceDashboardStrategy}async function P(e){let i=(await h()).SECTION_STRATEGIES?.[e];if(!i)throw new Error(`[maintenance-supporter] strategy bundle loaded but has no section strategy "${e}"`);return i}function B(e){return class extends HTMLElement{static async generate(i,l){return(await P(e)).generate(i,l)}}}var y=class extends HTMLElement{static getCreateSuggestions(s){return{title:"Maintenance Supporter",icon:"mdi:wrench-clock"}}static async getConfigElement(){return await h(),document.createElement(C)}static async generate(s,i){return(await I()).generate(s,i)}};function O(){try{customElements.define(L,y)}catch{}for(let e of M)try{customElements.define(`ll-strategy-section-${e}`,B(e))}catch{}}O();var m=window;m.customStrategies=m.customStrategies||[];var U=[{type:f,strategyType:"dashboard",name:"Maintenance Supporter",description:"Auto-generated dashboard. Group views by area, status, floor, or due date \u2014 picked from the strategy editor or YAML.",documentationURL:"https://github.com/iluebbe/maintenance_supporter/blob/master/docs/EXAMPLES.md#dashboard-strategy"},{type:"maintenance-supporter-section",strategyType:"section",name:"Maintenance Supporter \u2014 Section",description:"Embed maintenance tasks (filterable by area, status, due date) as a section in any dashboard view.",documentationURL:"https://github.com/iluebbe/maintenance_supporter/blob/master/docs/EXAMPLES.md#section-strategy"},{type:"maintenance-supporter-vacation",strategyType:"section",name:"Maintenance Supporter \u2014 Vacation Status",description:"Compact vacation-mode status banner. Tap to open settings in the panel."},{type:"maintenance-supporter-budget",strategyType:"section",name:"Maintenance Supporter \u2014 Budget Status",description:"Monthly + yearly maintenance budget overview. Tap for details."},{type:"maintenance-supporter-groups",strategyType:"section",name:"Maintenance Supporter \u2014 Groups",description:"List of configured task groups with member counts."}];for(let e of U)m.customStrategies.some(s=>s.type===e.type&&s.strategyType===e.strategyType)||m.customStrategies.push(e);(()=>{let e=window;if(e.__msStrategyHealActive)return;e.__msStrategyHealActive=!0;let s=/^\/(auth|config|developer-tools|profile|hassio|history|logbook|map|media-browser|energy|todo|calendar)\b/,i=/Timeout waiting for strategy element ll-strategy-(dashboard-)?maintenance-supporter/i,l=`custom:${f}`;function S(r){let t=[document.documentElement],a=0;for(;t.length&&a<9e3;){let o=t.pop();if(a++,!o)continue;let n=o;if(n.nodeType===1&&n.tagName&&n.tagName.toLowerCase()===r)return n;n.shadowRoot&&t.push(n.shadowRoot);let u=o.children;if(u)for(let p of Array.from(u))t.push(p)}return null}function R(r){let t=r?.views;if(!Array.isArray(t)||!t.length)return null;let a=window.location.pathname.split("/").filter(Boolean).pop()||"",o=t.find(u=>u?.path===a);if(o)return o;let n=Number(a);return Number.isInteger(n)&&t[n]?t[n]:t[0]}function k(){try{let t=S("ha-panel-lovelace")?.lovelace;if(!t)return!1;let a=o=>o?.type;for(let o of[t.config,t.rawConfig]){if(!o)continue;if(a(o.strategy)===l)return!0;let n=R(o);if(n&&a(n.strategy)===l)return!0}return!1}catch{return!1}}function T(){let r=!1,t=0,a=!1,o=!1,n=[document.documentElement],u=0;for(;n.length&&u<9e3;){let p=n.pop();if(u++,!p)continue;let d=p;if(d.nodeType===1&&d.tagName){let c=d.tagName.toLowerCase();(c==="hui-view"||c==="hui-sections-view")&&(r=!0),(c==="ha-card"||c==="hui-card")&&t++,c==="hui-empty-state-card"&&(o=!0),c==="hui-error-card"&&i.test(d.textContent||"")&&(a=!0)}d.shadowRoot&&n.push(d.shadowRoot);let _=p.children;if(_)for(let c of Array.from(_))n.push(c)}return a?!0:o?!1:r&&t<3&&k()}let N="/maintenance_supporter_strategy_shim.js",E=0,b=0;function A(){let r=Date.now();r-b<5e3||E>=3||(b=r,E+=1,import(`${N}?heal=${r}`).catch(()=>{}).finally(()=>{let t=window.location.pathname+window.location.search;history.pushState(null,"","/lovelace"),window.dispatchEvent(new CustomEvent("location-changed")),window.setTimeout(()=>{history.pushState(null,"",t),window.dispatchEvent(new CustomEvent("location-changed"))},200)}))}function g(){if(s.test(window.location.pathname))return;let r=0,t=Date.now(),a=window.setInterval(()=>{r++;try{if(Date.now()-t<6e3)return;if(s.test(window.location.pathname)){window.clearInterval(a);return}T()?A():window.clearInterval(a),r>=30&&window.clearInterval(a)}catch{window.clearInterval(a)}},500)}try{document.readyState==="loLine truncated
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
var v="2.99.0";var f="maintenance-supporter",L=`ll-strategy-dashboard-${f}`,C="hui-maintenance-supporter-strategy-editor",M=["maintenance-supporter-section","maintenance-supporter-vacation","maintenance-supporter-budget","maintenance-supporter-groups"],D=`/maintenance_supporter_strategy/maintenance-dashboard-strategy.js?v=${v}`,w=null;function h(){return w||(w=import(D)),w}async function I(){let e=await h();if(!e.MaintenanceDashboardStrategy)throw new Error("[maintenance-supporter] strategy bundle loaded but did not export MaintenanceDashboardStrategy");return e.MaintenanceDashboardStrategy}async function P(e){let i=(await h()).SECTION_STRATEGIES?.[e];if(!i)throw new Error(`[maintenance-supporter] strategy bundle loaded but has no section strategy "${e}"`);return i}function B(e){return class extends HTMLElement{static async generate(i,l){return(await P(e)).generate(i,l)}}}var y=class extends HTMLElement{static getCreateSuggestions(s){return{title:"Maintenance Supporter",icon:"mdi:wrench-clock"}}static async getConfigElement(){return await h(),document.createElement(C)}static async generate(s,i){return(await I()).generate(s,i)}};function O(){try{customElements.define(L,y)}catch{}for(let e of M)try{customElements.define(`ll-strategy-section-${e}`,B(e))}catch{}}O();var m=window;m.customStrategies=m.customStrategies||[];var U=[{type:f,strategyType:"dashboard",name:"Maintenance Supporter",description:"Auto-generated dashboard. Group views by area, status, floor, or due date \u2014 picked from the strategy editor or YAML.",documentationURL:"https://github.com/iluebbe/maintenance_supporter/blob/master/docs/EXAMPLES.md#dashboard-strategy"},{type:"maintenance-supporter-section",strategyType:"section",name:"Maintenance Supporter \u2014 Section",description:"Embed maintenance tasks (filterable by area, status, due date) as a section in any dashboard view.",documentationURL:"https://github.com/iluebbe/maintenance_supporter/blob/master/docs/EXAMPLES.md#section-strategy"},{type:"maintenance-supporter-vacation",strategyType:"section",name:"Maintenance Supporter \u2014 Vacation Status",description:"Compact vacation-mode status banner. Tap to open settings in the panel."},{type:"maintenance-supporter-budget",strategyType:"section",name:"Maintenance Supporter \u2014 Budget Status",description:"Monthly + yearly maintenance budget overview. Tap for details."},{type:"maintenance-supporter-groups",strategyType:"section",name:"Maintenance Supporter \u2014 Groups",description:"List of configured task groups with member counts."}];for(let e of U)m.customStrategies.some(s=>s.type===e.type&&s.strategyType===e.strategyType)||m.customStrategies.push(e);(()=>{let e=window;if(e.__msStrategyHealActive)return;e.__msStrategyHealActive=!0;let s=/^\/(auth|config|developer-tools|profile|hassio|history|logbook|map|media-browser|energy|todo|calendar)\b/,i=/Timeout waiting for strategy element ll-strategy-(dashboard-)?maintenance-supporter/i,l=`custom:${f}`;function S(r){let t=[document.documentElement],a=0;for(;t.length&&a<9e3;){let o=t.pop();if(a++,!o)continue;let n=o;if(n.nodeType===1&&n.tagName&&n.tagName.toLowerCase()===r)return n;n.shadowRoot&&t.push(n.shadowRoot);let u=o.children;if(u)for(let p of Array.from(u))t.push(p)}return null}function R(r){let t=r?.views;if(!Array.isArray(t)||!t.length)return null;let a=window.location.pathname.split("/").filter(Boolean).pop()||"",o=t.find(u=>u?.path===a);if(o)return o;let n=Number(a);return Number.isInteger(n)&&t[n]?t[n]:t[0]}function k(){try{let t=S("ha-panel-lovelace")?.lovelace;if(!t)return!1;let a=o=>o?.type;for(let o of[t.config,t.rawConfig]){if(!o)continue;if(a(o.strategy)===l)return!0;let n=R(o);if(n&&a(n.strategy)===l)return!0}return!1}catch{return!1}}function T(){let r=!1,t=0,a=!1,o=!1,n=[document.documentElement],u=0;for(;n.length&&u<9e3;){let p=n.pop();if(u++,!p)continue;let d=p;if(d.nodeType===1&&d.tagName){let c=d.tagName.toLowerCase();(c==="hui-view"||c==="hui-sections-view")&&(r=!0),(c==="ha-card"||c==="hui-card")&&t++,c==="hui-empty-state-card"&&(o=!0),c==="hui-error-card"&&i.test(d.textContent||"")&&(a=!0)}d.shadowRoot&&n.push(d.shadowRoot);let _=p.children;if(_)for(let c of Array.from(_))n.push(c)}return a?!0:o?!1:r&&t<3&&k()}let N="/maintenance_supporter_strategy_shim.js",E=0,b=0;function A(){let r=Date.now();r-b<5e3||E>=3||(b=r,E+=1,import(`${N}?heal=${r}`).catch(()=>{}).finally(()=>{let t=window.location.pathname+window.location.search;history.pushState(null,"","/lovelace"),window.dispatchEvent(new CustomEvent("location-changed")),window.setTimeout(()=>{history.pushState(null,"",t),window.dispatchEvent(new CustomEvent("location-changed"))},200)}))}function g(){if(s.test(window.location.pathname))return;let r=0,t=Date.now(),a=window.setInterval(()=>{r++;try{if(Date.now()-t<6e3)return;if(s.test(window.location.pathname)){window.clearInterval(a);return}T()?A():window.clearInterval(a),r>=30&&window.clearInterval(a)}catch{window.clearInterval(a)}},500)}try{document.readyState==="loLine truncated
|
||||
+301
@@ -0,0 +1,301 @@
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
import{e as y}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-PCR5FRSH.js";import{d as m}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-RHKFQ4Q2.js";import{a as h}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-GM2CHGSG.js";import{H as b,a as o,b as u,c as n,f as l,h as v,l as g,m as d,s as r,u as f}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3FGYBPXY.js";var a=class extends v{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._sensors=[];this._selected=new Set;this._objectNames={};this._objects=[];this._covered={};this._users=[];this._responsible="";this._forMinutes="0";this._localeReady=!1;this._userService=null;this._toggle=t=>{let i=new Set(this._selected);i.has(t)?i.delete(t):i.add(t),this._selected=i};this._toggleAll=()=>{this._selected.size===this._sensors.length?this._selected=new Set:this._selected=new Set(this._sensors.map(t=>t.entity_id))};this._adopt=async()=>{if(this._selected.size===0||this._adopting)return;this._error="";let t=this._sensors.filter(e=>this._selected.has(e.entity_id)).map(e=>({entity_id:e.entity_id,name:e.name,entry_id:this._existingEntryFor(e)??void 0,object_name:this._effectiveName(e),device_id:e.device_id??void 0,part_id:e.suggested_part_id??void 0,responsible_user_id:this._responsible||void 0,for_minutes:parseInt(this._forMinutes,10)>0?parseInt(this._forMinutes,10):void 0})),i=await h(this,{type:"maintenance_supporter/problem_sensors/adopt",selections:t},{busy:e=>{this._adopting=e},onError:e=>{this._error=e}});i!==void 0&&(this.dispatchEvent(new CustomEvent("problem-sensors-adopted",{bubbles:!0,composed:!0,detail:i})),this._open=!1)}}get _lang(){return b(this.hass)}updated(t){t.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,f(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._sensors=[],this._selected=new Set,this._objectNames={},this._responsible="",this._forMinutes="0",this._userService?this._userService.updateHass(this.hass):this._userService=new y(this.hass);let[t,i,e]=await Promise.all([h(this,{type:"maintenance_supporter/problem_sensors/discover"},{onError:s=>{this._error=s}}),this._userService.getUsers().catch(()=>[]),this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"}).catch(()=>({objects:[]}))]);this._loading=!1,t!==void 0&&(this._objects=(e?.objects||[]).filter(s=>!s.object.archived_at).map(s=>({entry_id:s.entry_id,name:s.object.name,tasks:(s.tasks||[]).length})),this._sensors=t?.sensors||[],this._covered=Object.fromEntries(this._sensors.map(s=>[s.entity_id,s.covered_by??null])),this._selected=new Set(this._sensors.filter(s=>!s.covered_by).map(s=>s.entity_id)),this._users=i)}_defaultName(t){return t.suggested_entry_id?t.suggested_object_name:t.candidate?.name??t.suggested_object_name}_effectiveName(t){return(this._objectNames[t.entity_id]??"").trim()||this._defaultName(t)}_existingEntryFor(t){let i=this._effectiveName(t),e=t.suggested_entry_id||t.target_entry_id||null;if(i===this._defaultName(t)&&e)return e;let s=this._objects.find(c=>c.name.trim().toLowerCase()===i.toLowerCase());return s?s.entry_id:null}_schedulePreview(){clearTimeout(this._previewTimer),this._previewTimer=setTimeout(()=>{this._preview()},350)}async _preview(){let t=this._sensors.map(e=>({entity_id:e.entity_id,name:e.name,entry_id:this._existingEntryFor(e)})),i=await h(this,{type:"maintenance_supporter/problem_sensors/preview",selections:t},{onError:()=>{}});i?.covered&&(this._covered={...this._covered,...i.covered})}_summary(t){let i=new Map;for(let e of this._sensors){if(!this._selected.has(e.entity_id))continue;let s=this._existingEntryFor(e),c=this._effectiveName(e),_=s??`new:${c.toLowerCase()}`,p=i.get(_)??{name:c,entry:s,adding:0};p.adding+=1,i.set(_,p)}return i.size===0?l:n`
|
||||
<div class="summary">
|
||||
<div class="summary-title">${r("adopt_summary_title",t)}</div>
|
||||
${[...i.values()].map(e=>{if(!e.entry)return n`<div class="summary-line new">
|
||||
${r("setups_new_object_count",t).replace("{name}",e.name).replace("{count}",String(e.adding))}
|
||||
</div>`;let s=this._objects.find(c=>c.entry_id===e.entry)?.tasks??0;return n`<div class="summary-line">
|
||||
${r("adopt_summary_existing",t).replace("{name}",e.name).replace("{before}",String(s)).replace("{after}",String(s+e.adding))}
|
||||
</div>`})}
|
||||
</div>
|
||||
`}_close(){clearTimeout(this._previewTimer),this._open=!1}_targetLabel(t,i){if(t.candidate&&this._existingEntryFor(t)===t.candidate.entry_id){let e=t.candidate.reasons.map(s=>r(`setups_reason_${s}`,i)).join(", ");return r("setups_target_match",i).replace("{name}",t.candidate.name).replace("{reasons}",e)}return this._effectiveName(t)}render(){if(!this._open)return n``;let t=this._lang,i=this._sensors.length>0&&this._selected.size===this._sensors.length;return n`
|
||||
<div class="overlay" @click=${this._close}>
|
||||
<div class="card" @click=${e=>e.stopPropagation()}>
|
||||
<div class="title">${r("adopt_problem_title",t)}</div>
|
||||
<div class="hint">${r("adopt_problem_hint",t)}</div>
|
||||
${this._error?n`<div class="error">${this._error}</div>`:l}
|
||||
|
||||
${this._loading?n`<div class="loading">…</div>`:this._sensors.length===0?n`<div class="empty">${r("adopt_problem_none",t)}</div>`:n`
|
||||
<label class="select-all">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${i}
|
||||
@change=${this._toggleAll}
|
||||
/>
|
||||
<span>${r("selected",t)}: ${this._selected.size} / ${this._sensors.length}</span>
|
||||
</label>
|
||||
<div class="row-sub adopt-object-hint">${r("adopt_object_hint",t)}</div>
|
||||
<datalist id="adopt-object-names">
|
||||
${this._objects.map(e=>n`<option value=${e.name}></option>`)}
|
||||
</datalist>
|
||||
<div class="list">
|
||||
${this._sensors.map(e=>{let s=this._selected.has(e.entity_id),c=e.state==="on",_=[e.device_name,e.area_name].filter(Boolean).join(" \xB7 ");return n`
|
||||
<label class="row">
|
||||
<input
|
||||
type="checkbox"
|
||||
.checked=${s}
|
||||
@change=${()=>this._toggle(e.entity_id)}
|
||||
/>
|
||||
<div class="row-main">
|
||||
<div class="row-top">
|
||||
<span class="row-name">${e.name}</span>
|
||||
<span class="chip ${c?"chip-active":"chip-ok"}">
|
||||
${c?r("adopt_problem_active",t):r("adopt_problem_ok",t)}
|
||||
</span>
|
||||
</div>
|
||||
${_?n`<div class="row-sub">${_}</div>`:l}
|
||||
${s?n`<div class="row-object" @click=${p=>p.stopPropagation()}>
|
||||
<input
|
||||
class="adopt-object"
|
||||
list="adopt-object-names"
|
||||
aria-label=${r("object",t)}
|
||||
placeholder=${this._defaultName(e)}
|
||||
.value=${this._objectNames[e.entity_id]??this._defaultName(e)}
|
||||
@input=${p=>{this._objectNames={...this._objectNames,[e.entity_id]:p.target.value},this._schedulePreview()}}
|
||||
/>
|
||||
</div>`:l}
|
||||
<div class="row-target">
|
||||
→ ${this._targetLabel(e,t)}${this._existingEntryFor(e)?l:n` <span class="new-tag">${r("adopt_problem_new_object",t)}</span>`}
|
||||
</div>
|
||||
${this._covered[e.entity_id]?n`<div class="maybe">
|
||||
${r("setups_maybe_covered",t).replace("{name}",this._covered[e.entity_id].name)}
|
||||
</div>`:l}
|
||||
${e.suggested_part_name?n`<div class="row-part">
|
||||
<ha-icon icon="mdi:package-variant-closed"></ha-icon>
|
||||
${r("adopt_problem_part",t).replace("{name}",e.suggested_part_name)}
|
||||
</div>`:l}
|
||||
</div>
|
||||
</label>
|
||||
`})}
|
||||
</div>
|
||||
`}
|
||||
|
||||
${!this._loading&&this._sensors.length>0?this._summary(t):l}
|
||||
|
||||
${!this._loading&&this._sensors.length>0?n`
|
||||
<label class="responsible">
|
||||
<span>${r("for_at_least_minutes",t)}</span>
|
||||
<input
|
||||
class="for-input"
|
||||
type="number"
|
||||
min=${m[0]}
|
||||
max=${m[1]}
|
||||
.value=${this._forMinutes}
|
||||
@input=${e=>this._forMinutes=e.target.value}
|
||||
/>
|
||||
</label>
|
||||
<div class="for-hint">${r("adopt_for_minutes_hint",t)}</div>
|
||||
`:l}
|
||||
|
||||
${!this._loading&&this._sensors.length>0&&this._users.length>0?n`
|
||||
<label class="responsible">
|
||||
<span>${r("adopt_problem_responsible",t)}</span>
|
||||
<select
|
||||
.value=${this._responsible}
|
||||
@change=${e=>{this._responsible=e.target.value}}
|
||||
>
|
||||
<option value="" ?selected=${!this._responsible}>${r("no_user_assigned",t)}</option>
|
||||
${this._users.map(e=>n`<option value=${e.id} ?selected=${e.id===this._responsible}>${e.name}</option>`)}
|
||||
</select>
|
||||
</label>
|
||||
`:l}
|
||||
|
||||
<div class="actions">
|
||||
<ha-button appearance="plain" @click=${this._close}>
|
||||
${r("cancel",t)}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
@click=${this._adopt}
|
||||
.disabled=${this._selected.size===0||this._adopting}
|
||||
>
|
||||
${r("adopt_problem_adopt",t)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`}};a.styles=u`
|
||||
.overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
min-width: min(360px, calc(100vw - 24px));
|
||||
max-width: 560px;
|
||||
width: 90vw;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.hint {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
.error {
|
||||
color: var(--error-color, #f44336);
|
||||
font-size: 13px;
|
||||
}
|
||||
.loading,
|
||||
.empty {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 14px;
|
||||
padding: 12px 0;
|
||||
}
|
||||
.select-all {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
.select-all input {
|
||||
cursor: pointer;
|
||||
}
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
overflow-y: auto;
|
||||
max-height: 50vh;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.row input {
|
||||
margin-top: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.row-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.row-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.row-name {
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
}
|
||||
.row-sub {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
.row-object { margin-top: 6px; }
|
||||
.row-object input {
|
||||
width: 100%; box-sizing: border-box; padding: 6px 8px; font: inherit;
|
||||
border: 1px solid var(--divider-color); border-radius: 6px;
|
||||
background: var(--card-background-color); color: var(--primary-text-color);
|
||||
}
|
||||
.adopt-object-hint { margin: 0 0 8px; }
|
||||
.row-target {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
}
|
||||
.row-part {
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.row-part ha-icon {
|
||||
--mdc-icon-size: 14px;
|
||||
}
|
||||
.new-tag {
|
||||
font-style: italic;
|
||||
}
|
||||
.maybe {
|
||||
font-size: 11px;
|
||||
color: var(--warning-color, #ff9800);
|
||||
}
|
||||
.summary {
|
||||
font-size: 12px;
|
||||
border-top: 1px dashed var(--divider-color);
|
||||
padding-top: 6px;
|
||||
}
|
||||
.summary-title {
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.summary-line {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
.chip {
|
||||
font-size: 11px;
|
||||
padding: 1px 8px;
|
||||
border-radius: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.chip-active {
|
||||
background: var(--error-color, #f44336);
|
||||
color: #fff;
|
||||
}
|
||||
.chip-ok {
|
||||
background: var(--divider-color);
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.responsible {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.for-input {
|
||||
width: 70px;
|
||||
background: var(--card-background-color);
|
||||
color: var(--primary-text-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 4px;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
.for-hint {
|
||||
font-size: 11px;
|
||||
color: var(--secondary-text-color);
|
||||
margin: -4px 0 2px;
|
||||
}
|
||||
.responsible select {
|
||||
flex: 1;
|
||||
min-width: 140px;
|
||||
padding: 4px 6px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--divider-color);
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
`,o([g({attribute:!1})],a.prototype,"hass",2),o([d()],a.prototype,"_open",2),o([d()],a.prototype,"_loading",2),o([d()],a.prototype,"_adopting",2),o([d()],a.prototype,"_error",2),o([d()],a.prototype,"_sensors",2),o([d()],a.prototype,"_selected",2),o([d()],a.prototype,"_objectNames",2),o([d()],a.prototype,"_objects",2),o([d()],a.prototype,"_covered",2),o([d()],a.prototype,"_users",2),o([d()],a.prototype,"_responsible",2),o([d()],a.prototype,"_forMinutes",2);customElements.get("maintenance-adopt-problem-sensors-dialog")||customElements.define("maintenance-adopt-problem-sensors-dialog",a);export{a as MaintenanceAdoptProblemSensorsDialog};
|
||||
@@ -0,0 +1,279 @@
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
import{a as X}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-MQRW7LAF.js";import{a as o}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-5MYBEQC2.js";import{f as E,g as M,h as z,i as Q,j as U,k as Y,l as q,m as G,n as R,o as A,p as W}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-MGSZZ3YF.js";import{a as B}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-EKN6HBJN.js";import{e as P,f as K}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-DRT62KZF.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-SYPCMUXO.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-55ND3GKE.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-RUYEMIDM.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-JQXHX4U4.js";import{B as S,H as F,K as g,L as v,O as N,P as I,T as w,V as T,a as $,c as p,f as y,h as H,l as k,m as j,n as C,q as D,s as a,u as L,y as O}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3FGYBPXY.js";function J(b,i,t,s){let l=b.entries.filter(e=>e.type==="completed").sort((e,n)=>e.ts-n.ts),d=`${b.from?t.date(b.from):"\u2026"} \u2013 ${b.to?t.date(b.to):"\u2026"}`,r=b.totals,h=[[i.completions,t.number(r.completions)],[i.totalCost,t.cost(r.totalCost)],[i.avgCost,r.avgCost!=null?t.cost(r.avgCost):i.none],[i.totalTime,r.totalDuration?t.duration(r.totalDuration):i.none]].map(([e,n])=>`<div class="kpi"><div class="k">${o(e)}</div><div class="v">${o(n)}</div></div>`).join(""),u=b.byObject.map(e=>`<tr>
|
||||
<td>${o(e.objectName)}</td>
|
||||
<td class="num">${o(t.number(e.completions))}</td>
|
||||
<td class="num">${o(t.cost(e.cost))}</td>
|
||||
<td class="num">${o(t.share(e.share))}</td>
|
||||
</tr>`).join(""),f=b.buckets.buckets.reduce((e,n)=>Math.max(e,n.cost),0),c=b.buckets.buckets.map(e=>{let n=f>0?Math.round(e.cost/f*100):0;return`<tr>
|
||||
<td class="nowrap">${o(t.bucket(e))}</td>
|
||||
<td class="num">${o(t.number(e.completions))}</td>
|
||||
<td class="bar-cell">${n>0?`<span class="bar" style="width:${n}%"></span>`:""}</td>
|
||||
<td class="num">${o(t.cost(e.cost))}</td>
|
||||
</tr>`}).join(""),m=l.map(e=>{let n=[e.notes,e.completedBy?`${i.completedBy}: ${e.completedBy}`:null].filter(Boolean).join(" \xB7 "),x=e.phaseName?`${e.taskName} \xB7 ${e.phaseName}`:e.taskName;return`<tr>
|
||||
<td class="nowrap">${o(t.date(e.timestamp))}</td>
|
||||
<td>${o(e.objectName)}</td>
|
||||
<td>${o(x)}</td>
|
||||
<td class="num">${e.cost!=null?o(t.cost(e.cost)):o(i.none)}</td>
|
||||
<td class="num">${e.duration!=null?o(t.duration(e.duration)):o(i.none)}</td>
|
||||
<td class="notes">${o(n)||o(i.none)}</td>
|
||||
</tr>`}).join(`
|
||||
`);return`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="color-scheme" content="light">
|
||||
<title>${o(i.title)} \u2014 ${o(b.areaName)}</title>
|
||||
<style>
|
||||
/* Printable sheet: it opens as a blob in whatever viewer the OS supplies
|
||||
(Companion = WebView, dark phones paint a dark default canvas), so the
|
||||
document states its own light scheme and paints its background. */
|
||||
:root { color-scheme: light; }
|
||||
* { box-sizing: border-box; }
|
||||
body { font: 13px/1.5 -apple-system, Segoe UI, Roboto, sans-serif; color: #1a1a1a; background: #fff; margin: 32px; }
|
||||
h1 { font-size: 22px; margin: 0 0 2px; }
|
||||
h2 { font-size: 15px; margin: 24px 0 8px; }
|
||||
.sub { color: #666; margin: 0 0 16px; }
|
||||
.kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin: 0 0 8px; }
|
||||
.kpi { border: 1px solid #ddd; border-radius: 6px; padding: 8px 10px; }
|
||||
.kpi .k { color: #777; font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; }
|
||||
.kpi .v { font-size: 16px; font-weight: 600; }
|
||||
table { border-collapse: collapse; width: 100%; }
|
||||
th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: #666; border-bottom: 2px solid #ccc; padding: 6px 8px; }
|
||||
td { border-bottom: 1px solid #e5e5e5; padding: 6px 8px; vertical-align: top; }
|
||||
td.num, th.num { text-align: right; white-space: nowrap; }
|
||||
td.nowrap { white-space: nowrap; }
|
||||
td.notes { color: #444; white-space: pre-wrap; }
|
||||
td.bar-cell { width: 40%; vertical-align: middle; }
|
||||
.bar { display: block; height: 10px; background: #9aa7b8; border-radius: 0 3px 3px 0;
|
||||
-webkit-print-color-adjust: exact; print-color-adjust: exact; }
|
||||
tfoot td { border-bottom: none; border-top: 2px solid #ccc; font-weight: 600; }
|
||||
tr { break-inside: avoid; }
|
||||
.cap-note { margin-top: 14px; color: #888; font-size: 11px; }
|
||||
@media print { body { margin: 12mm; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>${o(i.title)} \u2014 ${o(b.areaName)}</h1>
|
||||
<p class="sub">${o(i.period)}: ${o(d)} \xB7 ${o(i.generated)} ${o(t.date(s))}</p>
|
||||
<div class="kpis">${h}</div>
|
||||
|
||||
<h2>${o(i.costPerObject)}</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>${o(i.colObject)}</th><th class="num">${o(i.completions)}</th>
|
||||
<th class="num">${o(i.colCost)}</th><th class="num">${o(i.share)}</th>
|
||||
</tr></thead>
|
||||
<tbody>${u||`<tr><td colspan="4">${o(i.none)}</td></tr>`}</tbody>
|
||||
<tfoot><tr>
|
||||
<td>${o(i.totalCost)}</td><td class="num">${o(t.number(r.completions))}</td>
|
||||
<td class="num">${o(t.cost(r.totalCost))}</td><td></td>
|
||||
</tr></tfoot>
|
||||
</table>
|
||||
|
||||
<h2>${o(i.costPerBucket)}</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>${o(i.period)}</th><th class="num">${o(i.completions)}</th>
|
||||
<th></th><th class="num">${o(i.colCost)}</th>
|
||||
</tr></thead>
|
||||
<tbody>${c||`<tr><td colspan="4">${o(i.none)}</td></tr>`}</tbody>
|
||||
</table>
|
||||
|
||||
<h2>${o(i.historyHeading)} (${o(t.number(l.length))})</h2>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>${o(i.colDate)}</th><th>${o(i.colObject)}</th><th>${o(i.colTask)}</th>
|
||||
<th class="num">${o(i.colCost)}</th><th class="num">${o(i.colDuration)}</th><th>${o(i.colNotes)}</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
${m||`<tr><td colspan="6">${o(i.none)}</td></tr>`}
|
||||
</tbody>
|
||||
<tfoot><tr>
|
||||
<td colspan="3">${o(i.totalCost)}</td>
|
||||
<td class="num">${o(t.cost(r.totalCost))}</td>
|
||||
<td class="num">${r.totalDuration?o(t.duration(r.totalDuration)):""}</td><td></td>
|
||||
</tr></tfoot>
|
||||
</table>
|
||||
${b.capped?`<p class="cap-note">${o(i.capNote)}</p>`:""}
|
||||
</body>
|
||||
</html>`}var Z=25,V=5,tt={completed:C.ok,missed:C.overdue,reset:C.paused,skipped:"var(--secondary-text-color)"},_=class extends H{constructor(){super(...arguments);this.areaKey="";this.objects=[];this.showArchived=!1;this.currencySymbol="";this.userName=()=>null;this._from="";this._to="";this._entryFilter="";this._taskQuery="";this._expanded=!1;this._printing=!1;this._histories=new X(this);this._localeReady=!1;this._filtersFor=null;this._memo=null}createRenderRoot(){return this}get _lang(){return F(this.hass)}willUpdate(t){if(super.willUpdate(t),t.has("areaKey")&&this._filtersFor!==this.areaKey){this._filtersFor=this.areaKey;let s=R();this._from=s.from,this._to=s.to,this._entryFilter="",this._taskQuery="",this._expanded=!1}if(t.has("objects")||t.has("areaKey")||t.has("showArchived")||t.has("hass")&&t.get("hass")===void 0){let s=this._areaObjects().flatMap(l=>l.tasks.map(d=>({entryId:l.entry_id,task:d})));this._histories.sync(this.hass,s)}}updated(t){super.updated(t),!this._localeReady&&this.hass&&(this._localeReady=!0,L(this._lang).then(()=>this.requestUpdate()))}_areaObjects(){return z(this.objects,this.areaKey,this.showArchived).sort((t,s)=>t.object.name.localeCompare(s.object.name))}_entries(t){let s=[this.objects,this.areaKey,this.showArchived,this._histories.version];if(this._memo&&this._memo.deps.every((d,r)=>d===s[r]))return this._memo.entries;let l=Q(t,(d,r)=>this._histories.historyOf(d,r));return this._memo={deps:s,entries:l},l}_figures(){let t=this._areaObjects(),s=this._entries(t),l={from:this._from||null,to:this._to||null},d=U(s,{...l,entryId:this._entryFilter||null,taskQuery:this._taskQuery}),r=this._entryFilter?t.filter(h=>h.entry_id===this._entryFilter):t;return{objects:t,entries:s,filtered:d,totals:Y(d),buckets:G(d,l),byObject:q(d,r)}}_setRange(t){this._from=t.from,this._to=t.to,this._expanded=!1}_openObject(t){this.dispatchEvent(new CustomEvent("open-object",{detail:{entryId:t},bubbles:!0,composed:!0}))}_openTask(t,s){this.dispatchEvent(new CustomEvent("open-task",{detail:{entryId:t,taskId:s},bubbles:!0,composed:!0}))}_bucketLabel(t,s){return t.month==null?String(t.year):`${T(t.month,this._lang,s)} ${t.year}`}_countText(t,s,l){let d=this._lang;return l===1?a(s,d):a(t,d).replace("{n}",g(l,d))}async _print(){if(this._printing)return;let t=K();this._printing=!0;try{await this._histories.settled()}finally{this._printing=!1}let s=this._lang,l=this.currencySymbol,{filtered:d,totals:r,buckets:h,byObject:u}=this._figures(),f={title:a("area_report_title",s),generated:a("report_generated",s),period:a("area_report_period",s),completions:a("area_kpi_completions",s),totalCost:a("total_cost",s),avgCost:a("avg_cost",s),totalTime:a("area_kpi_total_time",s),costPerObject:a("area_cost_per_object",s),costPerBucket:h.unit==="month"?a("area_cost_per_month",s):a("area_cost_per_year",s),share:a("area_cost_share",s),historyHeading:a("area_history_section",s),colDate:a("date",s),colObject:a("object",s),colTask:a("task_name",s),colCost:a("cost",s),colDuration:a("duration",s),colNotes:a("notes_label",s),completedBy:a("completed_by",s),capNote:a("object_history_cap_note",s),none:"\u2014"},c=d.filter(n=>n.type==="completed"),m=d.map(n=>({...n,completedBy:n.completedBy?this.userName(n.completedBy):null,notes:n.notes?B(n.notes,s):null})),e=J({areaName:E(this.areaKey,this.hass?.areas,a("no_area",s)),from:this._from||S(c[c.length-1]?.timestamp)||null,to:this._to||O(),totals:r,byObject:u,buckets:h,entries:m,capped:this._histories.capped},f,{date:n=>N(n,s),cost:n=>v(n,l,s),duration:n=>w(n,s),bucket:n=>this._bucketLabel(n,"long"),share:n=>`${g(n*100,s,0)} %`,number:n=>g(n,s)},new Date().toISOString());P(e,t)}_renderRangeChips(t){let s=this._lang,l=O(),d=R(l),r=u=>this._from===u.from&&this._to===u.to,h=W(t,Number(l.slice(0,4))).slice(0,V);return p`
|
||||
<div class="filter-chips area-range-chips">
|
||||
<button class="filter-chip ${r(d)?"active":""}" @click=${()=>this._setRange(R())}>
|
||||
${a("area_range_12m",s)}
|
||||
</button>
|
||||
${h.map(u=>p`
|
||||
<button class="filter-chip ${r(A(u))?"active":""}" @click=${()=>this._setRange(A(u))}>${u}</button>
|
||||
`)}
|
||||
<button class="filter-chip ${!this._from&&!this._to?"active":""}" @click=${()=>this._setRange({from:"",to:""})}>
|
||||
${a("area_range_all",s)}
|
||||
</button>
|
||||
</div>
|
||||
`}_renderChart(t){let s=this._lang,l=this.currencySymbol,d=t.reduce((c,m)=>Math.max(c,m.cost),0),r=t.reduce((c,m)=>Math.min(c,m.cost),0);if(d<=0&&r>=0)return p`<p class="area-empty">${a("area_no_costs",s)}</p>`;let h=c=>Math.round(c/(d-r)*1e3)/10,u=Math.max(1,Math.ceil(t.length/12)),f=t.map((c,m)=>({b:c,i:m})).filter(({i:c})=>c%u===0);return p`
|
||||
<div class="area-chart">
|
||||
<div class="area-chart-max">${v(d,l,s)}</div>
|
||||
<div class="area-chart-bars">
|
||||
${r<0?p`<div class="area-chart-zero" style="bottom: ${h(-r)}%"></div>`:y}
|
||||
${t.map(c=>{let m=`${this._bucketLabel(c,"long")}: ${v(c.cost,l,s)} \xB7 ${a("area_kpi_completions",s)}: ${g(c.completions,s)}`,e=c.cost<0?h(c.cost-r):h(-r);return p`<div class="area-bar" title=${m} aria-label=${m}>
|
||||
${c.cost!==0?p`<div class="area-bar-fill ${c.cost<0?"credit":""}" style="height: ${h(Math.abs(c.cost))}%${e?`; bottom: ${e}%`:""}"></div>`:y}
|
||||
</div>`})}
|
||||
</div>
|
||||
${r<0?p`<div class="area-chart-min">${v(r,l,s)}</div>`:y}
|
||||
<div class="area-chart-axis ${u>1?"grouped":""}">
|
||||
${f.map(({b:c,i:m})=>p`<span class="area-bar-label" style="flex-grow: ${Math.min(u,t.length-m)}">${c.month==null?String(c.year):p`${T(c.month,s,"short")}${c.month===0||m===0?p`<br />${c.year}`:y}`}</span>`)}
|
||||
</div>
|
||||
</div>
|
||||
`}render(){if(!this.hass||!this.areaKey)return y;let t=this._lang,s=this.currencySymbol,{objects:l,entries:d,filtered:r,totals:h,buckets:u,byObject:f}=this._figures(),c=l.reduce((e,n)=>e+n.tasks.filter(x=>this.showArchived||!x.archived).length,0),m=this._expanded?r:r.slice(0,Z);return p`
|
||||
<div class="detail-section area-view">
|
||||
<div class="detail-header">
|
||||
<h2 class="area-title">
|
||||
<ha-icon .icon=${M(this.areaKey,this.hass.areas)}></ha-icon>
|
||||
${E(this.areaKey,this.hass.areas,a("no_area",t))}
|
||||
</h2>
|
||||
<div class="action-buttons">
|
||||
<ha-button appearance="plain" class="area-print" .disabled=${this._printing} @click=${()=>this._print()}>
|
||||
<ha-icon icon="mdi:printer-outline"></ha-icon>
|
||||
${this._printing?a("loading",t):a("area_report_print",t)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="meta">
|
||||
${this._countText("area_object_count","area_object_count_one",l.length)} ·
|
||||
${this._countText("templates_task_count","templates_task_count_one",c)}
|
||||
${this._histories.loading?p` · <span class="area-loading">${a("loading",t)}</span>`:y}
|
||||
</p>
|
||||
|
||||
${this._renderRangeChips(d)}
|
||||
<div class="filter-bar area-filters">
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
.hass=${this.hass}
|
||||
.lang=${t}
|
||||
.label=${a("date_from",t)}
|
||||
.value=${this._from}
|
||||
@value-changed=${e=>{this._from=e.detail.value}}
|
||||
></ms-date-field>
|
||||
<ms-date-field
|
||||
kind="date"
|
||||
clearable
|
||||
.hass=${this.hass}
|
||||
.lang=${t}
|
||||
.label=${a("date_to",t)}
|
||||
.value=${this._to}
|
||||
@value-changed=${e=>{this._to=e.detail.value}}
|
||||
></ms-date-field>
|
||||
<label class="filter-field">
|
||||
<span class="filter-label">${a("object",t)}</span>
|
||||
<select class="area-object-filter" .value=${this._entryFilter}
|
||||
@change=${e=>{this._entryFilter=e.target.value}}>
|
||||
<option value="" ?selected=${!this._entryFilter}>${a("all_objects",t)}</option>
|
||||
${l.map(e=>p`<option value=${e.entry_id} ?selected=${e.entry_id===this._entryFilter}>${e.object.name}</option>`)}
|
||||
</select>
|
||||
</label>
|
||||
<input
|
||||
type="search"
|
||||
class="search-input area-task-filter"
|
||||
aria-label=${a("area_task_filter",t)}
|
||||
placeholder=${a("area_task_filter",t)}
|
||||
.value=${this._taskQuery}
|
||||
@input=${e=>{this._taskQuery=e.target.value}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="kpi-bar area-kpis">
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("area_kpi_completions",t)}</div>
|
||||
<div class="kpi-value-large area-kpi-completions">${g(h.completions,t)}</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("total_cost",t)}</div>
|
||||
<div class="kpi-value-large area-kpi-cost">${v(h.totalCost,s,t)}</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("avg_cost",t)}</div>
|
||||
<div class="kpi-value-large">${h.avgCost!=null?v(h.avgCost,s,t):"\u2014"}</div>
|
||||
</div>
|
||||
<div class="kpi-card">
|
||||
<div class="kpi-label">${a("area_kpi_total_time",t)}</div>
|
||||
<div class="kpi-value-large">${h.totalDuration?w(h.totalDuration,t):"\u2014"}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3>${u.unit==="month"?a("area_cost_per_month",t):a("area_cost_per_year",t)}</h3>
|
||||
${this._renderChart(u.buckets)}
|
||||
|
||||
<h3>${a("area_cost_per_object",t)}</h3>
|
||||
<div class="objects-table-wrap">
|
||||
<table class="objects-table area-object-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>${a("object",t)}</th>
|
||||
<th class="num">${a("area_kpi_completions",t)}</th>
|
||||
<th class="num">${a("cost",t)}</th>
|
||||
<th class="num">${a("area_cost_share",t)}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${f.map(e=>p`
|
||||
<tr class="objects-table-row" @click=${()=>this._openObject(e.entryId)}>
|
||||
<td><span class="objects-table-name">${e.objectName}</span></td>
|
||||
<td class="num">${g(e.completions,t)}</td>
|
||||
<td class="num">${v(e.cost,s,t)}</td>
|
||||
<td class="num">
|
||||
<span class="area-share"><span class="area-share-fill" style="width: ${Math.round(e.share*100)}%"></span></span>
|
||||
${g(e.share*100,t,0)} %
|
||||
</td>
|
||||
</tr>
|
||||
`)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h3>
|
||||
${a("area_history_section",t)}
|
||||
<span class="area-count">${g(r.length,t)}</span>
|
||||
</h3>
|
||||
${r.length===0?p`<p class="area-empty">${a("object_history_empty",t)}</p>`:p`
|
||||
<div class="objects-table-wrap">
|
||||
<table class="objects-table area-history-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>${a("date",t)}</th>
|
||||
<th></th>
|
||||
<th>${a("object",t)}</th>
|
||||
<th>${a("task_name",t)}</th>
|
||||
<th class="num">${a("cost",t)}</th>
|
||||
<th class="num">${a("duration",t)}</th>
|
||||
<th>${a("notes_label",t)}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${m.map(e=>{let n=e.notes?B(e.notes,t):"";return p`
|
||||
<tr class="objects-table-row area-history-row" @click=${()=>this._openTask(e.entryId,e.taskId)}>
|
||||
<td title=${I(e.timestamp,t)}>${N(e.timestamp,t)}</td>
|
||||
<td>
|
||||
<span class="area-entry-type" style="color: ${tt[e.type]??"inherit"}">
|
||||
<ha-icon .icon=${D[e.type]||"mdi:circle"}></ha-icon>${a(e.type,t)}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="area-object-link" @click=${x=>{x.stopPropagation(),this._openObject(e.entryId)}}>
|
||||
${e.objectName}
|
||||
</button>
|
||||
</td>
|
||||
<td><button class="area-task-link">${e.taskName}${e.phaseName?` \xB7 ${e.phaseName}`:""}</button></td>
|
||||
<td class="num">${e.cost!=null?v(e.cost,s,t):"\u2014"}</td>
|
||||
<td class="num">${e.duration!=null?w(e.duration,t):"\u2014"}</td>
|
||||
<td class="oc-notes" title=${n}>${n||"\u2014"}</td>
|
||||
</tr>
|
||||
`})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
${r.length>m.length?p`<ha-button appearance="plain" class="area-more" @click=${()=>{this._expanded=!0}}>
|
||||
${a("show_all",t)} (${g(r.length,t)})
|
||||
</ha-button>`:y}
|
||||
`}
|
||||
${this._histories.capped?p`<p class="area-cap-note">${a("object_history_cap_note",t)}</p>`:y}
|
||||
</div>
|
||||
`}};$([k({attribute:!1})],_.prototype,"hass",2),$([k()],_.prototype,"areaKey",2),$([k({attribute:!1})],_.prototype,"objects",2),$([k({type:Boolean})],_.prototype,"showArchived",2),$([k()],_.prototype,"currencySymbol",2),$([k({attribute:!1})],_.prototype,"userName",2),$([j()],_.prototype,"_from",2),$([j()],_.prototype,"_to",2),$([j()],_.prototype,"_entryFilter",2),$([j()],_.prototype,"_taskQuery",2),$([j()],_.prototype,"_expanded",2),$([j()],_.prototype,"_printing",2);customElements.get("maintenance-area-view")||customElements.define("maintenance-area-view",_);export{_ as MaintenanceAreaView};
|
||||
@@ -0,0 +1,54 @@
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
import{a as O}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-MQRW7LAF.js";import{q as j,r as E,s as L,t as R,u as C}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-MGSZZ3YF.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-SYPCMUXO.js";import{a as _,b as S,c as A}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-HFZZVTW7.js";import"/maintenance_supporter_panelfiles/panel-chunks/chunk-55ND3GKE.js";import{H as g,K as k,L as v,O as w,a as l,c as a,f as m,h as y,l as p,m as b,s as r,u as $,y as f}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3FGYBPXY.js";var q=[{key:"name",label:"area",num:!1},{key:"objects",label:"objects",num:!0},{key:"tasks",label:"tasks",num:!0},{key:"overdue",label:"overdue",num:!0},{key:"due_soon",label:"due_soon",num:!0},{key:"cost_year",label:"area_cost_year",num:!0},{key:"cost_total",label:"total_cost",num:!0},{key:"last",label:"last_performed",num:!1}],i=class extends y{constructor(){super(...arguments);this.objects=[];this.showArchived=!1;this.currencySymbol="";this._query="";this._sort=E(S(_.areaSort));this._histories=new O(this);this._localeReady=!1;this._memo=null}createRenderRoot(){return this}get _lang(){return g(this.hass)}willUpdate(t){if(super.willUpdate(t),t.has("objects")||t.has("showArchived")||t.has("hass")&&t.get("hass")===void 0){let o=this.objects.filter(s=>this.showArchived||!s.object.archived).flatMap(s=>s.tasks.map(n=>({entryId:s.entry_id,task:n})));this._histories.sync(this.hass,o)}}updated(t){super.updated(t),!this._localeReady&&this.hass&&(this._localeReady=!0,$(this._lang).then(()=>this.requestUpdate()))}_rows(t){let o=this._lang,s=[this.objects,this.showArchived,this._histories.version,this.hass?.areas,o,t];if(this._memo&&this._memo.deps.every((c,h)=>c===s[h]))return this._memo.rows;let n=j(this.objects,{showArchived:this.showArchived,areas:this.hass?.areas,noAreaLabel:r("no_area",o),year:t,historyOf:(c,h)=>this._histories.historyOf(c,h)});return this._memo={deps:s,rows:n},n}_setSort(t){this._sort=L(this._sort,t),A(_.areaSort,`${this._sort.key}:${this._sort.dir}`)}_open(t){this.dispatchEvent(new CustomEvent("open-area",{detail:{areaKey:t},bubbles:!0,composed:!0}))}_toggleArchived(){this.dispatchEvent(new CustomEvent("archived-toggle",{bubbles:!0,composed:!0}))}_headerLabel(t,o,s){return t==="area_cost_year"?r("area_cost_year",s).replace("{year}",String(o)):r(t,s)}render(){if(!this.hass)return m;let t=this._lang,o=Number(f().slice(0,4)),s=this.currencySymbol,n=this._rows(o),c=R(C(n,this._query),this._sort),h=this.objects.filter(e=>e.object.archived).length,d=e=>k(e,t);return a`
|
||||
<div class="filter-bar area-filter-bar">
|
||||
<input
|
||||
type="search"
|
||||
class="search-input"
|
||||
aria-label=${r("areas_filter",t)}
|
||||
placeholder=${r("areas_filter",t)}
|
||||
.value=${this._query}
|
||||
@input=${e=>{this._query=e.target.value}}
|
||||
/>
|
||||
${this._histories.loading?a`<span class="area-loading">${r("loading",t)}</span>`:m}
|
||||
${h>0?a`<ha-button class="archived-toggle ${this.showArchived?"active":""}" @click=${()=>this._toggleArchived()}>
|
||||
<ha-icon icon="mdi:archive-outline"></ha-icon>
|
||||
${this.showArchived?r("hide_archived",t):`${r("show_archived",t)} (${h})`}
|
||||
</ha-button>`:m}
|
||||
</div>
|
||||
${n.length===0?a`<div class="empty-state">${r("no_objects",t)}</div>`:c.length===0?a`<div class="empty-state">${r("areas_filter_none",t)}</div>`:a`
|
||||
<div class="objects-table-wrap">
|
||||
<table class="objects-table areas-table">
|
||||
<thead>
|
||||
<tr>
|
||||
${q.map(e=>{let u=this._sort.key===e.key;return a`<th class=${e.num?"num":""}
|
||||
aria-sort=${u?this._sort.dir==="asc"?"ascending":"descending":"none"}>
|
||||
<button class="area-sort ${u?"active":""}" title=${r("sort_label",t)} @click=${()=>this._setSort(e.key)}>
|
||||
${this._headerLabel(e.label,o,t)}
|
||||
${u?a`<ha-icon icon=${this._sort.dir==="asc"?"mdi:arrow-up":"mdi:arrow-down"}></ha-icon>`:m}
|
||||
</button>
|
||||
</th>`})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${c.map(e=>a`
|
||||
<tr class="objects-table-row" tabindex="0"
|
||||
@click=${()=>this._open(e.key)}
|
||||
@keydown=${u=>{u.key==="Enter"&&this._open(e.key)}}>
|
||||
<td>
|
||||
<ha-icon class="area-row-icon" .icon=${e.icon}></ha-icon>
|
||||
<span class="objects-table-name">${e.name}</span>
|
||||
</td>
|
||||
<td class="num">${d(e.objects)}</td>
|
||||
<td class="num">${d(e.tasks)}</td>
|
||||
<td class="num">${e.overdue?a`<span class="area-count overdue">${d(e.overdue)}</span>`:d(0)}</td>
|
||||
<td class="num">${e.dueSoon?a`<span class="area-count due-soon">${d(e.dueSoon)}</span>`:d(0)}</td>
|
||||
<td class="num">${v(e.costYear,s,t)}</td>
|
||||
<td class="num">${v(e.costTotal,s,t)}</td>
|
||||
<td>${e.lastCompletion?w(e.lastCompletion,t):"\u2014"}</td>
|
||||
</tr>
|
||||
`)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`}
|
||||
`}};l([p({attribute:!1})],i.prototype,"hass",2),l([p({attribute:!1})],i.prototype,"objects",2),l([p({type:Boolean})],i.prototype,"showArchived",2),l([p()],i.prototype,"currencySymbol",2),l([b()],i.prototype,"_query",2),l([b()],i.prototype,"_sort",2);customElements.get("maintenance-areas-view")||customElements.define("maintenance-areas-view",i);export{i as MaintenanceAreasView};
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
import"/maintenance_supporter_panelfiles/panel-chunks/chunk-TX2GMEMJ.js";import{H as u,a as n,b as _,c as o,f as p,h as c,l as g,m as a,s}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3FGYBPXY.js";var d=["round_robin","least_completed","random"],v=["low","normal","high"],m=365,i=class extends c{constructor(){super(...arguments);this._open=!1;this._mode="assign";this._count=0;this._users=[];this._present=[];this._assignKind="one";this._person="";this._pool=new Set;this._rotation=d[0];this._addText="";this._remove=new Set;this._setWarning=!1;this._warning=7;this._setPriority=!1;this._priority="normal";this._setMute=!1;this._mute=!1;this._resolve=null}get _lang(){return u(this.hass)}open(t,e,r){return this._resolve?.(null),this._mode=t,this._count=e.length,this._users=[...r].sort((l,h)=>l.name.localeCompare(h.name)),this._present=[...new Set(e.flatMap(l=>l.labels||[]))].sort((l,h)=>l.localeCompare(h)),this._assignKind="one",this._person="",this._pool=new Set,this._rotation=d[0],this._addText="",this._remove=new Set,this._setWarning=!1,this._warning=7,this._setPriority=!1,this._priority="normal",this._setMute=!1,this._mute=!1,this._open=!0,new Promise(l=>{this._resolve=l})}_finish(t){this._open=!1;let e=this._resolve;this._resolve=null,e?.(t)}changes(){if(this._mode==="assign"){if(this._assignKind==="one")return{responsible_user_id:this._person||null,assignee_pool:null,rotation_strategy:null};let e=this._users.map(r=>r.id).filter(r=>this._pool.has(r));return e.length<2?null:{assignee_pool:e,rotation_strategy:this._rotation,responsible_user_id:null}}if(this._mode==="labels"){let e=[...new Set(this._addText.split(",").map(l=>l.trim()).filter(Boolean))],r=[...this._remove];return e.length||r.length?{labels_add:e,labels_remove:r}:null}let t={};return this._setWarning&&(t.warning_days=this._warning),this._setPriority&&(t.priority=this._priority),this._setMute&&(t.notify_enabled=!this._mute),Object.keys(t).length?t:null}_toggle(t,e){let r=new Set(t);return r.has(e)?r.delete(e):r.add(e),r}_renderAssign(t){return o`
|
||||
<div class="hint">${s("bulk_assign_hint",t)}</div>
|
||||
<label class="choice">
|
||||
<input type="radio" name="kind" .checked=${this._assignKind==="one"} @change=${()=>this._assignKind="one"} />
|
||||
${s("bulk_assign_one",t)}
|
||||
</label>
|
||||
${this._assignKind==="one"?o`<select class="person" .value=${this._person} @change=${e=>this._person=e.target.value}>
|
||||
<option value="" ?selected=${!this._person}>${s("unassigned",t)}</option>
|
||||
${this._users.map(e=>o`<option value=${e.id} ?selected=${this._person===e.id}>${e.name}</option>`)}
|
||||
</select>`:p}
|
||||
<label class="choice">
|
||||
<input type="radio" name="kind" .checked=${this._assignKind==="rotate"} @change=${()=>this._assignKind="rotate"} />
|
||||
${s("bulk_assign_rotate",t)}
|
||||
</label>
|
||||
${this._assignKind==="rotate"?o`<div class="pool">
|
||||
${this._users.map(e=>o`<label class="row">
|
||||
<input type="checkbox" .checked=${this._pool.has(e.id)} @change=${()=>this._pool=this._toggle(this._pool,e.id)} />
|
||||
${e.name}
|
||||
</label>`)}
|
||||
</div>
|
||||
<label class="field">
|
||||
<span>${s("rotation_strategy",t)}</span>
|
||||
<select class="rotation" .value=${this._rotation} @change=${e=>this._rotation=e.target.value}>
|
||||
${d.map(e=>o`<option value=${e} ?selected=${this._rotation===e}>${s(`rotation_${e}`,t)}</option>`)}
|
||||
</select>
|
||||
</label>
|
||||
${this._pool.size<2?o`<div class="hint">${s("bulk_rotation_min_two",t)}</div>`:p}`:p}
|
||||
`}_renderLabels(t){return o`
|
||||
<ms-textfield
|
||||
class="labels-add"
|
||||
label=${s("bulk_labels_add",t)}
|
||||
.helper=${s("labels_help",t)}
|
||||
.value=${this._addText}
|
||||
@input=${e=>this._addText=e.target.value}
|
||||
></ms-textfield>
|
||||
${this._present.length?o`<div class="section-title">${s("bulk_labels_remove",t)}</div>
|
||||
<div class="pool">
|
||||
${this._present.map(e=>o`<label class="row">
|
||||
<input type="checkbox" .checked=${this._remove.has(e)} @change=${()=>this._remove=this._toggle(this._remove,e)} />
|
||||
${e}
|
||||
</label>`)}
|
||||
</div>`:p}
|
||||
`}_renderEdit(t){return o`
|
||||
<div class="hint">${s("bulk_edit_hint",t)}</div>
|
||||
<div class="row edit-row">
|
||||
<input type="checkbox" class="set-warning" .checked=${this._setWarning} @change=${e=>this._setWarning=e.target.checked} />
|
||||
<span class="grow">${s("warning_days",t)}</span>
|
||||
<input type="number" class="warning" min="0" max=${m} .value=${String(this._warning)} ?disabled=${!this._setWarning}
|
||||
@change=${e=>{let r=parseInt(e.target.value,10);this._warning=Number.isInteger(r)?Math.min(Math.max(r,0),m):this._warning}} />
|
||||
</div>
|
||||
<div class="row edit-row">
|
||||
<input type="checkbox" class="set-priority" .checked=${this._setPriority} @change=${e=>this._setPriority=e.target.checked} />
|
||||
<span class="grow">${s("priority",t)}</span>
|
||||
<select class="priority" .value=${this._priority} ?disabled=${!this._setPriority} @change=${e=>this._priority=e.target.value}>
|
||||
${v.map(e=>o`<option value=${e} ?selected=${this._priority===e}>${s(`priority_${e}`,t)}</option>`)}
|
||||
</select>
|
||||
</div>
|
||||
<div class="row edit-row">
|
||||
<input type="checkbox" class="set-mute" .checked=${this._setMute} @change=${e=>this._setMute=e.target.checked} />
|
||||
<span class="grow">${s("bulk_no_notifications",t)}</span>
|
||||
<input type="checkbox" class="mute" .checked=${this._mute} ?disabled=${!this._setMute} @change=${e=>this._mute=e.target.checked} />
|
||||
</div>
|
||||
`}render(){if(!this._open)return o``;let t=this._lang,e=this._mode==="assign"?s("bulk_assign",t):this._mode==="labels"?s("labels",t):s("edit",t),r=this.changes()!==null;return o`
|
||||
<ha-dialog open @closed=${()=>this._finish(null)}>
|
||||
<div class="dialog-title">${e}</div>
|
||||
<div class="content">
|
||||
<div class="count">${s("bulk_n_selected",t).replace("{n}",String(this._count))}</div>
|
||||
${this._mode==="assign"?this._renderAssign(t):this._mode==="labels"?this._renderLabels(t):this._renderEdit(t)}
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<ha-button appearance="plain" @click=${()=>this._finish(null)}>${s("cancel",t)}</ha-button>
|
||||
<ha-button class="apply" .disabled=${!r} @click=${()=>this._finish(this.changes())}>${s("save",t)}</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}};i.styles=_`
|
||||
.dialog-title { font-size: 18px; font-weight: 500; padding-bottom: 12px; }
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
min-width: min(360px, calc(100vw - 24px));
|
||||
max-width: 520px;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.content { min-width: 0; max-width: none; max-height: none; }
|
||||
}
|
||||
.count, .hint { color: var(--secondary-text-color); font-size: 13px; }
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
}
|
||||
.choice, .row { display: flex; align-items: center; gap: 8px; font-size: 14px; cursor: pointer; }
|
||||
.pool { display: flex; flex-direction: column; gap: 4px; padding-left: 26px; }
|
||||
.field { display: flex; align-items: center; gap: 8px; justify-content: space-between; font-size: 14px; }
|
||||
.grow { flex: 1; min-width: 0; }
|
||||
select, input[type="number"] {
|
||||
font: inherit;
|
||||
padding: 4px 6px;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 4px;
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
max-width: 100%;
|
||||
}
|
||||
input[type="number"] { width: 72px; }
|
||||
select.person { margin-left: 26px; }
|
||||
.dialog-actions { display: flex; justify-content: flex-end; gap: 8px; padding-top: 16px; }
|
||||
`,n([g({attribute:!1})],i.prototype,"hass",2),n([a()],i.prototype,"_open",2),n([a()],i.prototype,"_mode",2),n([a()],i.prototype,"_count",2),n([a()],i.prototype,"_users",2),n([a()],i.prototype,"_present",2),n([a()],i.prototype,"_assignKind",2),n([a()],i.prototype,"_person",2),n([a()],i.prototype,"_pool",2),n([a()],i.prototype,"_rotation",2),n([a()],i.prototype,"_addText",2),n([a()],i.prototype,"_remove",2),n([a()],i.prototype,"_setWarning",2),n([a()],i.prototype,"_warning",2),n([a()],i.prototype,"_setPriority",2),n([a()],i.prototype,"_priority",2),n([a()],i.prototype,"_setMute",2),n([a()],i.prototype,"_mute",2);customElements.get("maintenance-bulk-edit-dialog")||customElements.define("maintenance-bulk-edit-dialog",i);export{i as MaintenanceBulkEditDialog};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,2 @@
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
import{s as a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3FGYBPXY.js";var _=[{key:"name",labelKey:"name",required:!0},{key:"ref_no",labelKey:"ref_number"},{key:"manufacturer",labelKey:"manufacturer"},{key:"model",labelKey:"model"},{key:"serial_number",labelKey:"serial_number_label"},{key:"installation_date",labelKey:"installed"},{key:"warranty_expiry",labelKey:"warranty"},{key:"area_id",labelKey:"area"},{key:"documentation_url",labelKey:"documentation_url_label"},{key:"notes",labelKey:"object_notes_label"},{key:"task_count",labelKey:"tasks"},{key:"actions",labelKey:"actions"}],u=_.map(e=>e.key),i=["name","manufacturer","model","serial_number","installation_date","warranty_expiry","area_id","task_count","actions"];function g(e){if(!Array.isArray(e))return[...i];let n=new Set,r=[];for(let t of e)typeof t=="string"&&u.includes(t)&&!n.has(t)&&(n.add(t),r.push(t));return r.length?(r.includes("name")||r.unshift("name"),r):[...i]}function f(e,n){return a(`home_dwelling_${e==="house"||e==="apartment"?e:"unknown"}`,n)}function m(e,n){if(!e)return"";try{return new Intl.DisplayNames([n],{type:"region"}).of(e)||e}catch{return e}}var l={floors:"home_reason_floors",basement_floor:"home_reason_basement",area_basement:"home_reason_basement",area_garden:"home_reason_garden",area_shed:"home_reason_shed",area_driveway:"home_reason_driveway",area_garage:"home_reason_garage",entity_garage:"home_reason_garage",area_attic:"home_reason_attic",entity_lawn_mower:"home_reason_lawn_mower",entity_gate:"home_reason_gate",area_balcony:"home_reason_balcony",small_home:"home_reason_small_home"},c={ups:"home_reason_ups"};function d(e,n){let r=[];for(let t of e){let o=l[t];if(!o)continue;let s=a(o,n);r.includes(s)||r.push(s)}return r.join(", ")}function b(e,n,r){if(e==="starter")return a("home_reason_starter",n);if(e==="country")return a("home_reason_country",n).replace("{country}",m(r,n));if(e.startsWith("feature_")){let t=e.slice(8),o=l[`area_${t}`]??c[t];return a("home_reason_feature",n).replace("{feature}",o?a(o,n):e)}return a(`home_trait_${e}`,n)}export{_ as a,i as b,g as c,f as d,m as e,d as f,b as g};
|
||||
@@ -0,0 +1,2 @@
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
function c(n){return n==="use"?"use":"purchase"}var s=n=>typeof n=="number"&&Number.isFinite(n)?n:null;function a(n){let t=s(n?.cost);if(t===null)return null;let o=s(n?.package_size);return o&&o>0?t/o:t}function p(n,t){let o=s(t)&&t>0?t:s(n.restock_quantity)||1,e=s(n.package_size);return Math.round(o*(e&&e>0?e:1)*100)/100}function l(n){let t=s(n.cost);if(n.cost_basis!=="use")return t;let o=s(n.parts_cost),e=n.purchase?null:t;return e===null&&o===null?null:(e??0)+(o??0)}function _(n){if(!n)return[];let t=[],o=n.photo_doc_id;typeof o=="string"&&t.push(o);let e=n.photo_doc_ids;Array.isArray(e)&&t.push(...e);let r=[];for(let u of t){if(typeof u!="string")continue;let i=u.trim();if(!(!i||r.includes(i))&&(r.push(i),r.length>=10))break}return r}export{_ as a,c as b,a as c,p as d,l as e};
|
||||
@@ -0,0 +1,2 @@
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
function r(t){return String(t??"").replace(/[&<>"']/g,n=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[n])}export{r as a};
|
||||
@@ -0,0 +1,488 @@
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
import{d as M,e as R}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-WACUKJSI.js";import{a as L,b as z,c as O}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-HFZZVTW7.js";import{c as U}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-55ND3GKE.js";import{a as I,h as H,l as V,n as B}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-MNPRUCF5.js";import{j as Q,k as w}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-RHKFQ4Q2.js";import{a as j}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-GM2CHGSG.js";import{$ as C,A,K as E,L as q,M as F,Q as N,a,b as x,c as l,f,h as k,l as h,m as _,s as o,z as P}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3FGYBPXY.js";var T=class{constructor(p,e){this.host=p;this.opts=e;this.photos=[];this.uploadedIds=[];this.uploading=!1;this.errorKey="";this._session=0;this._uploadEntry=new Map;this.max=e.max??10,p.addController(this)}hostConnected(){}get ids(){return this.photos.map(p=>p.id)}get remaining(){return Math.max(this.max-this.photos.length,0)}get full(){return this.remaining===0}errorText(p){return this.errorKey?o(this.errorKey,p).replace("{max}",String(this.max)):""}clearError(){this.errorKey=""}reset(p=[]){this._endSession(),this._revokeAll(),this.photos=p.map(e=>({id:e,preview:""})),this.uploadedIds=[],this._uploadEntry.clear(),this.errorKey="",this.host.requestUpdate()}async addFiles(p){if(p.length===0)return;let e=this._session,i=this.opts.hass(),t=this.opts.entryId(),r=p.slice(0,this.remaining);this.uploading=!0,this.errorKey="",this.host.requestUpdate();try{for(let n of r){let s=await M(i,t,n);if(e!==this._session){R(i,t,[s]);return}this.uploadedIds=[...this.uploadedIds,s],this._uploadEntry.set(s,t),this.photos=[...this.photos,{id:s,preview:URL.createObjectURL(n)}],this.host.requestUpdate()}p.length>r.length&&(this.errorKey="photos_limit")}catch(n){if(e!==this._session)return;this.errorKey=n instanceof Error&&n.message==="doc_too_large"?"doc_too_large":"doc_upload_failed"}finally{e===this._session&&(this.uploading=!1,this.host.requestUpdate())}}remove(p){let e=this.photos.find(i=>i.id===p);e?.preview&&URL.revokeObjectURL(e.preview),this.photos=this.photos.filter(i=>i.id!==p),this.uploadedIds.includes(p)&&(this.uploadedIds=this.uploadedIds.filter(i=>i!==p),this._discard([p])),this.host.requestUpdate()}discardOrphans(){if(this._endSession(),this.uploadedIds.length===0)return;let p=this.uploadedIds;this.uploadedIds=[],this._discard(p)}markAttached(){this._endSession(),this.uploadedIds=[],this._uploadEntry.clear()}_endSession(){this._session++,this.uploading=!1}_discard(p){let e=new Map;for(let t of p){let r=this._uploadEntry.get(t)??this.opts.entryId();this._uploadEntry.delete(t),e.set(r,[...e.get(r)??[],t])}let i=this.opts.hass();for(let[t,r]of e)R(i,t,r)}_revokeAll(){for(let p of this.photos)p.preview&&URL.revokeObjectURL(p.preview)}};function S(){if(window.externalApp!==void 0)return!0;let p=typeof navigator<"u"&&navigator.userAgent||"";return/Home Assistant\//.test(p)&&/Android/.test(p)}function W(){if(!S())return!1;let g=typeof navigator<"u"?navigator.mediaDevices:void 0;return!!g&&typeof g.getUserMedia=="function"}var te=1920,ie=.88,se={retryMs:500,windowMs:5e3},re=new Set(["NotReadableError","AbortError","TrackStartError"]),K=80,J=/back|rear|environment|rück|hinten/i,G=/front|user|selfie|vorne/i;function Y(g){let p=/camera2?\s*(\d+)/i.exec(g);return p?Number(p[1]):Number.MAX_SAFE_INTEGER}function D(g,p){if(p){let i=g.find(t=>t.label===p);if(i)return i}return g.filter(i=>i.label&&J.test(i.label)).sort((i,t)=>Y(i.label)-Y(t.label))[0]??null}var X=g=>g?.name||"Error",ae=g=>g?.message||"",b=class extends k{constructor(){super(...arguments);this.lang="en";this._open=!1;this._busy=!1;this._switching=!1;this._devices=[];this._deviceIndex=-1;this._switchFailed=!1;this._copied=!1;this.timing={...se};this._facing="environment";this._stream=null;this._opening=!1;this._gen=0;this._refused=new Set;this._log=[];this._t0=0;this._lastError=""}async open(){if(this._open||this._opening)return;this._opening=!0;let e=++this._gen;try{await this._acquireAndShow(e)}finally{this._opening=!1}}_live(e){return e===this._gen&&this.isConnected}async _acquireAndShow(e){let i=typeof navigator<"u"?navigator.mediaDevices:void 0;if(!i||typeof i.getUserMedia!="function"){this._unavailable("no_media_devices");return}this._log=[],this._t0=Date.now(),this._lastError="",this._refused.clear(),this._switchFailed=!1,this._copied=!1,this._note(navigator.userAgent);let t=z(L.cameraLabel),r=await this._enumerate(i);if(!this._live(e))return;let n=D(r,t),s="refused";if(n&&(s=await this._openPatiently(i,e,this._byDevice(n))),s==="cancelled")return;if(s!=="ok"){if(s=await this._openPatiently(i,e,{video:{facingMode:{ideal:"environment"}},what:"facing environment"}),s==="cancelled")return;if(s!=="ok"){this._unavailable(this._lastError||"camera_unavailable");return}if(r=await this._enumerate(i),!this._live(e)){this._stopStream();return}}if(this._devLine truncated
|
||||
`),i=!1;try{navigator.clipboard?.writeText&&(await navigator.clipboard.writeText(e),i=!0)}catch{}if(!i){let t=this.shadowRoot?.querySelector(".details textarea");if(t){t.focus(),t.select();try{i=document.execCommand("copy")}catch{i=!1}}}this._copied=i}async _shoot(){let e=this._video;if(!(!e||this._busy||this._switching)){this._busy=!0;try{let i=e.videoWidth||640,t=e.videoHeight||480,r=Math.min(1,te/Math.max(i,t)),n=document.createElement("canvas");n.width=Math.round(i*r),n.height=Math.round(t*r);let s=n.getContext("2d");if(!s)throw new Error("no_canvas");s.drawImage(e,0,0,n.width,n.height);let d=await new Promise(v=>n.toBlob(v,"image/jpeg",ie));if(!d)throw new Error("no_blob");let m=new Date().toISOString().replace(/[-:]/g,"").slice(0,15),u=new File([d],`photo-${m}.jpg`,{type:"image/jpeg"});this.close(),this.dispatchEvent(new CustomEvent("photo-captured",{detail:{file:u},bubbles:!0,composed:!0}))}catch(i){this._unavailable(i instanceof Error?i.message:String(i))}finally{this._busy=!1}}}render(){if(!this._open)return f;let e=this.lang,i=this._busy||this._switching;return l`
|
||||
<div class="overlay" role="dialog" aria-modal="true" aria-label=${o("doc_camera",e)}>
|
||||
<video autoplay playsinline muted></video>
|
||||
${this._switching?l`<div class="switching" role="status">${o("camera_switching",e)}</div>`:f}
|
||||
${this._switchFailed?l`<div class="switch-note" role="status">
|
||||
<div>${o("camera_not_answered",e)}</div>
|
||||
<details class="details">
|
||||
<summary>${o("camera_diagnostics",e)}</summary>
|
||||
<textarea readonly rows="6" .value=${this._log.join(`
|
||||
`)}></textarea>
|
||||
<button type="button" class="copy" @click=${this._copyLog}>${o(this._copied?"camera_copied":"camera_copy",e)}</button>
|
||||
</details>
|
||||
</div>`:f}
|
||||
<div class="bar">
|
||||
<button type="button" class="cancel" @click=${this.close}>${o("cancel",e)}</button>
|
||||
${this._devices.length>1?l`<button type="button" class="switch" ?disabled=${i} title=${o("camera_switch_lens",e)} aria-label=${o("camera_switch_lens",e)} @click=${this._switchCamera}>
|
||||
<ha-icon icon="mdi:camera-flip-outline"></ha-icon>
|
||||
<span class="switch-pos">${this._deviceIndex>=0?`${this._deviceIndex+1}/${this._devices.length}`:`?/${this._devices.length}`}</span>
|
||||
</button>`:f}
|
||||
<button type="button" class="shoot" ?disabled=${i} @click=${this._shoot}>
|
||||
<ha-icon icon="mdi:camera"></ha-icon><span>${o("camera_capture_shoot",e)}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`}};b.styles=x`
|
||||
:host { display: contents; }
|
||||
.overlay {
|
||||
position: fixed; inset: 0; z-index: 10000;
|
||||
background: #000; color: #fff;
|
||||
display: flex; flex-direction: column;
|
||||
}
|
||||
video { flex: 1; min-height: 0; width: 100%; object-fit: contain; background: #000; }
|
||||
.bar {
|
||||
display: flex; justify-content: space-between; align-items: center; gap: 16px;
|
||||
padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
button {
|
||||
font: inherit; border-radius: 24px; padding: 10px 18px; cursor: pointer;
|
||||
display: inline-flex; align-items: center; gap: 8px;
|
||||
}
|
||||
.cancel { background: transparent; color: #fff; border: 1px solid rgba(255, 255, 255, 0.6); }
|
||||
.switch { background: transparent; color: #fff; border: 1px solid rgba(255, 255, 255, 0.6); padding: 10px 12px; }
|
||||
.switch[disabled] { opacity: 0.5; cursor: default; }
|
||||
.switch ha-icon { --mdc-icon-size: 22px; }
|
||||
.switch-pos { font-size: 12px; opacity: 0.85; }
|
||||
.switching {
|
||||
position: absolute; top: 45%; left: 50%; transform: translate(-50%, -50%);
|
||||
padding: 8px 16px; border-radius: 16px; background: rgba(0, 0, 0, 0.6); font-size: 14px;
|
||||
}
|
||||
.switch-note {
|
||||
position: absolute; left: 12px; right: 12px; bottom: 84px; max-height: 60%; overflow: auto;
|
||||
text-align: center; color: #fff; font-size: 13px; text-shadow: 0 1px 2px #000;
|
||||
}
|
||||
.details { margin-top: 6px; text-align: start; text-shadow: none; }
|
||||
.details summary { cursor: pointer; text-align: center; opacity: 0.85; }
|
||||
.details textarea {
|
||||
display: block; box-sizing: border-box; width: 100%; margin: 6px 0;
|
||||
font: 11px/1.35 monospace; color: #fff; background: rgba(255, 255, 255, 0.12);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3); border-radius: 6px; padding: 6px; resize: vertical;
|
||||
}
|
||||
.copy { background: transparent; color: #fff; border: 1px solid rgba(255, 255, 255, 0.6); padding: 6px 14px; font-size: 13px; }
|
||||
.shoot { background: var(--primary-color, #03a9f4); color: #fff; border: none; font-weight: 600; }
|
||||
.shoot[disabled] { opacity: 0.6; cursor: default; }
|
||||
.shoot ha-icon { --mdc-icon-size: 22px; }
|
||||
`,a([h({type:String})],b.prototype,"lang",2),a([_()],b.prototype,"_open",2),a([_()],b.prototype,"_busy",2),a([_()],b.prototype,"_switching",2),a([_()],b.prototype,"_devices",2),a([_()],b.prototype,"_deviceIndex",2),a([_()],b.prototype,"_switchFailed",2),a([_()],b.prototype,"_copied",2);customElements.get("ms-camera-capture")||customElements.define("ms-camera-capture",b);var Z=x`
|
||||
ms-photo-picker { display: contents; }
|
||||
.photo-pickers {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
.photo-pick {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border: 1px dashed var(--divider-color);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
width: fit-content;
|
||||
}
|
||||
.photo-pick:hover { border-color: var(--primary-color); }
|
||||
.photo-pick:focus-visible { outline: 2px solid var(--primary-color); outline-offset: 2px; }
|
||||
.photo-pick.disabled { opacity: 0.6; cursor: default; }
|
||||
.photo-pick input[type="file"] { display: none; }
|
||||
.photo-android-hint {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
margin-top: 4px;
|
||||
}
|
||||
`,y=class extends k{constructor(){super(...arguments);this.lang="en";this.disabled=!1;this.busy=!1;this.accept="image/*";this.showCamera=!0;this.showGallery=!0;this.compact=!1;this.remaining=1/0;this._singlePick=S();this._inAppCamera=W()}createRenderRoot(){return this}get _locked(){return this.disabled||this.busy}_emit(e){e.length!==0&&this.dispatchEvent(new CustomEvent("files-picked",{detail:{files:e},bubbles:!0,composed:!0}))}_onInput(e){let i=e.target,t=Array.from(i.files??[]);i.value="",this._emit(t)}_onCameraClick(e){!this._inAppCamera||this._locked||(e.preventDefault(),this.querySelector("ms-camera-capture")?.open())}_onCameraUnavailable(){this._inAppCamera=!1,this.querySelector(".photo-pick-camera input")?.click()}_onKeydown(e){e.key!=="Enter"&&e.key!==" "||(e.preventDefault(),!this._locked&&e.currentTarget.click())}render(){if(this.remaining<=0||!this.showCamera&&!this.showGallery)return f;let e=this.lang,i=this._locked?"disabled":"",t=this.busy?o("uploading",e):o("doc_camera",e),r=o(this._singlePick?"choose_photo":"choose_photos",e);return l`
|
||||
<div class="photo-pickers">
|
||||
${this.showCamera?l`<label class="photo-pick photo-pick-camera ${i}" role="button" tabindex="0"
|
||||
aria-label=${t} title=${this.compact?t:f}
|
||||
@click=${this._onCameraClick} @keydown=${this._onKeydown}>
|
||||
<ha-icon icon="mdi:camera"></ha-icon>
|
||||
${this.compact?f:l`<span>${t}</span>`}
|
||||
<input type="file" accept=${this.accept} capture="environment"
|
||||
?disabled=${this._locked} @change=${this._onInput} />
|
||||
</label>`:f}
|
||||
${this.showGallery?l`<label class="photo-pick photo-pick-gallery ${i}" role="button" tabindex="0"
|
||||
aria-label=${r} title=${this.compact?r:f}
|
||||
@keydown=${this._onKeydown}>
|
||||
<ha-icon icon="mdi:image-multiple"></ha-icon>
|
||||
${this.compact?f:l`<span>${r}</span>`}
|
||||
<input type="file" accept=${this.accept} ?multiple=${!this._singlePick}
|
||||
?disabled=${this._locked} @change=${this._onInput} />
|
||||
</label>`:f}
|
||||
</div>
|
||||
${this.showGallery&&this._singlePick?l`<div class="photo-android-hint">${o("photos_android_hint",e)}</div>`:f}
|
||||
${this.showCamera&&this._inAppCamera?l`<ms-camera-capture .lang=${e}
|
||||
@photo-captured=${n=>this._emit([n.detail.file])}
|
||||
@capture-unavailable=${this._onCameraUnavailable}></ms-camera-capture>`:f}
|
||||
`}};a([h({type:String})],y.prototype,"lang",2),a([h({type:Boolean})],y.prototype,"disabled",2),a([h({type:Boolean})],y.prototype,"busy",2),a([h({type:String})],y.prototype,"accept",2),a([h({type:Boolean})],y.prototype,"showCamera",2),a([h({type:Boolean})],y.prototype,"showGallery",2),a([h({type:Boolean})],y.prototype,"compact",2),a([h({type:Number})],y.prototype,"remaining",2),a([_()],y.prototype,"_inAppCamera",2);customElements.get("ms-photo-picker")||customElements.define("ms-photo-picker",y);var $=Q,c=class extends k{constructor(){super(...arguments);this.entryId="";this.taskId="";this.taskName="";this.lang="en";this.checklist=[];this.adaptiveEnabled=!1;this.taskType="";this.readingUnit="";this.readings=[];this.readingHistory=[];this.restockDefault=null;this.restockUnitCost=null;this.restockPackage="";this.currencySymbol="";this.partsCostMode="purchase";this.parts=[];this.consumesParts=[];this.consumesInfo=[];this.requiredFields=[];this.phaseLabel="";this.requireTagScan=!1;this.viaTagScan=!1;this._open=!1;this._notes="";this._cost="";this._duration="";this._loading=!1;this._error="";this._checklistState={};this._feedback="needed";this._photos=new T(this,{entryId:()=>this.entryId,hass:()=>this.hass});this._readingValue="";this._readingValues={};this._restockQty="";this._completedAt="";this._usedParts={};this._usedQtyText={};this.checklistPrefill={}}open(e={}){this._open||(this._open=!0,this.viaTagScan=!!e.viaTagScan,this._notes="",this._cost="",this._duration="",this._error="",this._checklistState=Object.fromEntries(this.checklist.map((i,t)=>[String(t),!!this.checklistPrefill[i]]).filter(([,i])=>i)),this._feedback="needed",this._photos.reset(),this._readingValue="",this._readingValues={},this._restockQty=this.restockDefault!==null?String(this.restockDefault):"",this._completedAt="",this._usedParts=Object.fromEntries(this.consumesParts.map(i=>[I(i),{...i}])),this._usedQtyText={})}_rangeError(e,i){let t=this.lang;return o("settings_value_out_of_range",t).replace("{min}",E(e,t,{maximumFractionDigits:2})).replace("{max}",E(i,t,{maximumFractionDigits:2}))}_toggleCheck(e){let i=String(e);this._checklistState={...this._checklistState,[i]:!this._checklistState[i]}}_setFeedback(e){this._feedback=e}async _complete(){if(this._loading||this._photos.uploading)return;this._error="",this._photos.clearError();let e={type:"maintenance_supporter/task/complete",entry_id:this.entryId,task_id:this.taskId},i=this._notes.trim();if(i&&(e.notes=i),this._cost){let s=parseFloat(this._cost);isNaN(s)||(e.cost=s)}let t=V(this._duration);if(t!==null&&(e.duration=t),this.checklist.length>0&&(e.checklist_state=this._checklistState),this.adaptiveEnabled&&(e.feedback=this._feedback),this._photos.photos.length>0&&(e.photo_doc_ids=this._photos.ids),this.viaTagScan&&(e.via_tag_scan=!0),this._completedAt){if(A(this._completedAt)>Date.now()){this._error=o("completed_at_future_error",this.lang);return}e.completed_at=this._completedAt.length===16?`${this._completedAt}:00`:this._completedAt}if(this.readings.length>0){let s={};for(let d of this.readings){let m=(this._readingValues[d.id]??"").trim();if(m==="")continue;let u=parseFloat(m.replace(",","."));isNaN(u)||(s[d.id]=u)}Object.keys(s).length>0&&(e.reading_values=s)}else if(this._readingValue!==""){let s=parseFloat(this._readingValue);isNaN(s)||(e.reading_value=s)}if(this.restockDefault!==null&&this._restockQty.trim()!==""){let s=parseFloat(this._restockQty.replace(",","."));if(!Number.isFinite(s)||s<w[0]||s>w[1]){this._error=this._rangeError(...w);return}e.restock_quantity=s}if(this.parts.length>0){let s=[];for(let[d,m]of Object.entries(this._usedParts)){let u=this._usedQtyText[d],v=u===void 0?m.quantity:parseFloat(u.replace(",","."));if(!Number.isFinite(v)||v<$[0]||v>$[1]){this._error=this._rangeError(...$);return}s.push({...m,quantity:v})}e.used_parts=s.map(d=>d.entry_id?{part_id:d.part_id,quantity:d.quantity,entry_id:d.entry_id}:{part_id:d.part_id,quantity:d.quantity})}let r=await j(this,e,{busy:s=>{this._loading=s},lang:this.lang,fallbackKey:"save_error",onError:s=>{this._error=s}});if(r===void 0)return;this._photos.markAttached(),this._open=!1;let n={entryId:this.entryId,taskId:this.taskId,taskName:this.taskName,undo:r?.undo===!0};this.dispatchEvent(new CustomEvent("task-completed",{detail:n}))}_renderReadingField(e,i){let t=this._completedAt?A(this._completedAt):NaN,r=H(this.readingHistory,e.id,isNaN(t)?void 0:t),n=e.unit||this.readingUnit,s=(this._readingValues[e.id]??"").trim(),d=s===""?NaN:parseFloat(s.replace(",",".")),m=r!==void 0&&!isNaN(d)&&d<r.value,u=r!==void 0?E(r.value,i,{maximumFractionDigits:3}):"";return l`
|
||||
<label class="field reading-field">
|
||||
<span class="field-label">${e.name}${n?` (${n})`:""}</span>
|
||||
<input type="text" inputmode="decimal" class="field-input"
|
||||
placeholder=${r!==void 0?o("reading_last",i).replace("{value}",u):""}
|
||||
.value=${this._readingValues[e.id]??""}
|
||||
@input=${v=>{this._readingValues={...this._readingValues,[e.id]:v.target.value}}} />
|
||||
${m?l`<span class="reading-warn">${o("reading_below_last",i).replace("{value}",u)}</span>`:f}
|
||||
</label>`}get _missingRequired(){let e={notes:this._notes.trim()!=="",cost:this._cost.trim()!=="",duration:this._duration.trim()!=="",photo:this._photos.photos.length>0,user:!!this.hass?.user};return this.requiredFields.filter(i=>!e[i])}_demands(e){return this.requiredFields.includes(e)}_req(e){return this._demands(e)?l`<span class="req-mark" aria-hidden="true">*</span>`:f}_purchaseSuggestion(){if(this.restockDefault===null)return null;let e=parseFloat(this._restockQty);return this.restockUnitCost==null||!Number.isFinite(e)||e<=0?null:Math.round(this.restockUnitCost*e*100)/100}_partsValue(){if(this.restockDefault!==null||!this.parts.length)return null;let e=0,i=!1;for(let t of Object.values(this._usedParts)){let r=this.parts.find(s=>I({part_id:s.id,entry_id:s.entry_id})===I(t)),n=U(r);n!==null&&(e+=n*(t.quantity||1),i=!0)}return i?Math.round(e*100)/100:null}_renderCostSuggestion(e){let i=this._purchaseSuggestion();if(this.restockDefault!==null){let s=this.partsCostMode==="use"?l`<div class="cost-note">${o("cost_purchase_use_hint",e)}</div>`:f;if(this._cost.trim()!==""||i==null||i<=0)return s;let d=q(i,this.currencySymbol,e);return l`<button
|
||||
type="button"
|
||||
class="cost-suggestion"
|
||||
@click=${()=>this._cost=String(Math.round(i*100)/100)}
|
||||
>${o("cost_from_parts",e).replace("{amount}",d)}</button>${s}`}let t=this._partsValue();if(t==null||t<=0)return f;let r=q(t,this.currencySymbol,e),n=this.partsCostMode==="use"?"cost_parts_booked":"cost_parts_info";return l`<div class="cost-note">${o(n,e).replace("{amount}",r)}</div>`}_close(){this._open=!1,this._photos.discardOrphans()}_pickCompletedAt(){this._completedAt=`${P()}:00`}render(){if(!this._open)return l``;let e=this.lang||this.hass?.language||"en",i=this._error||this._photos.errorText(e);return l`
|
||||
<ha-dialog open @closed=${this._close}>
|
||||
<div class="dialog-title">${o("complete_title",e)}${this.taskName}</div>
|
||||
${this.phaseLabel?l`<div class="phase-line">${o("phase_current",e)}: ${this.phaseLabel}</div>`:f}
|
||||
${this.requireTagScan&&!this.viaTagScan?l`<div class="scan-required-note">${o("require_tag_scan_hint",e)}</div>`:f}
|
||||
<div class="content">
|
||||
${i?l`<div class="error">${i}</div>`:f}
|
||||
${this.checklist.length>0?l`
|
||||
<div class="checklist-section">
|
||||
<label class="checklist-label">${o("checklist",e)}</label>
|
||||
${this.checklist.map((t,r)=>l`
|
||||
<label class="checklist-item" @click=${()=>this._toggleCheck(r)}>
|
||||
<input type="checkbox" .checked=${!!this._checklistState[String(r)]} />
|
||||
<span>${t}</span>
|
||||
</label>
|
||||
`)}
|
||||
</div>
|
||||
`:f}
|
||||
${this.readings.length>0?l`<div class="readings-block">
|
||||
<span class="field-label">${o("readings_section",e)}</span>
|
||||
${this.readings.map(t=>this._renderReadingField(t,e))}
|
||||
</div>`:this.taskType==="reading"?l`
|
||||
<label class="field">
|
||||
<span class="field-label">${o("reading_value_label",e)}${this.readingUnit?` (${this.readingUnit})`:""}</span>
|
||||
<input type="number" step="any" class="field-input"
|
||||
.value=${this._readingValue}
|
||||
@input=${t=>this._readingValue=t.target.value} />
|
||||
</label>`:f}
|
||||
${this.parts.length?l`<div class="used-parts">
|
||||
<span class="field-label">${o("complete_parts_used",e)}</span>
|
||||
${this.parts.map(t=>{let r=I({part_id:t.id,entry_id:t.entry_id}),n=this._usedParts[r],s=n!==void 0,d=t.entry_id?{part_id:t.id,quantity:1,entry_id:t.entry_id}:{part_id:t.id,quantity:1};return l`<div class="used-part-row">
|
||||
<label class="used-part-check">
|
||||
<input type="checkbox" .checked=${s}
|
||||
@change=${m=>{let u={...this._usedParts};if(m.target.checked)u[r]=u[r]||d;else{delete u[r];let v={...this._usedQtyText};delete v[r],this._usedQtyText=v}this._usedParts=u}} />
|
||||
<span
|
||||
>${t.name}${t.owner_name?l`<span class="used-part-owner"> (${t.owner_name})</span>`:f}${t.stock!==null&&t.stock!==void 0?` (${F(t.stock,t.unit,e)})`:""}</span
|
||||
>
|
||||
</label>
|
||||
${s?l`<input class="used-part-qty" type="number" min=${$[0]} max=${$[1]} step="0.01"
|
||||
.value=${this._usedQtyText[r]??String(n.quantity)}
|
||||
@input=${m=>{let u=m.target.value;this._usedQtyText={...this._usedQtyText,[r]:u};let v=parseFloat(u.replace(",","."));Number.isFinite(v)&&v>=$[0]&&(this._usedParts={...this._usedParts,[r]:{...d,quantity:v}})}} />`:f}
|
||||
</div>`})}
|
||||
</div>`:this.consumesInfo.length?l`<div class="consumes-hint">
|
||||
${this.consumesInfo.map(t=>l`<div>${t}</div>`)}
|
||||
</div>`:f}
|
||||
${this.restockDefault!==null?l`
|
||||
<label class="field">
|
||||
<span class="field-label">${o("restock_quantity_label",e)}${this.restockPackage?` (\xD7 ${this.restockPackage})`:""}</span>
|
||||
<input type="number" step="0.01" min=${w[0]} max=${w[1]} class="field-input"
|
||||
.value=${this._restockQty}
|
||||
@input=${t=>this._restockQty=t.target.value} />
|
||||
</label>`:f}
|
||||
<!-- Native <input>s rather than <ha-textfield>: when this dialog
|
||||
is opened from a Lovelace card via dialog-mount, ha-textfield
|
||||
isn't yet registered (HA loads it lazily when its own panels
|
||||
need it) so the elements render with zero height and the user
|
||||
only sees the title + Cancel/Complete buttons — the original
|
||||
bug report. Native inputs always render. -->
|
||||
<label class="field">
|
||||
<span class="field-label">${this._demands("notes")?o("notes_label",e):o("notes_optional",e)}${this._req("notes")}</span>
|
||||
<!-- Several lines (#202): what was done, one line each. The
|
||||
history and the printouts keep the line breaks; they show
|
||||
notes as plain text, so no Markdown hint here. -->
|
||||
<textarea class="field-input notes-input" rows="3"
|
||||
.value=${this._notes}
|
||||
@input=${t=>this._notes=t.target.value}></textarea>
|
||||
</label>
|
||||
<div class="field">
|
||||
<span class="field-label">${this._demands("cost")?o("cost",e):o("cost_optional",e)}${this._req("cost")}</span>
|
||||
<ms-cost-input .lang=${e} .value=${this._cost}
|
||||
@value-changed=${t=>this._cost=t.detail.value}></ms-cost-input>
|
||||
${this._renderCostSuggestion(e)}
|
||||
</div>
|
||||
<label class="field">
|
||||
<span class="field-label">${this._demands("duration")?o("quick_complete_defaults_duration",e):o("duration_minutes",e)}${this._req("duration")}</span>
|
||||
<input type="number" step="1" min="0" inputmode="numeric" class="field-input"
|
||||
.value=${this._duration}
|
||||
@input=${t=>this._duration=t.target.value} />
|
||||
</label>
|
||||
<div class="field">
|
||||
<span class="field-label">${o("completed_at_optional",e)}</span>
|
||||
${this._completedAt?l`<ms-date-field
|
||||
kind="datetime"
|
||||
clearable
|
||||
.hass=${this.hass}
|
||||
.lang=${e}
|
||||
.value=${this._completedAt}
|
||||
.helper=${N(e)}
|
||||
@value-changed=${t=>this._completedAt=t.detail.value}
|
||||
></ms-date-field>`:l`<button type="button" class="backdate-pick" @click=${this._pickCompletedAt}>
|
||||
<ha-icon icon="mdi:calendar-clock"></ha-icon>${o("completed_at_pick",e)}
|
||||
</button>`}
|
||||
</div>
|
||||
<div class="field">
|
||||
<span class="field-label">${this._demands("photo")?o("completion_photos",e):o("completion_photos_optional",e)}${this._req("photo")}</span>
|
||||
${this._photos.photos.length>0?l`<div class="photo-strip">
|
||||
${this._photos.photos.map(t=>l`
|
||||
<div class="photo-preview">
|
||||
<img src=${t.preview} alt="" />
|
||||
<button type="button" class="photo-remove" @click=${()=>this._photos.remove(t.id)}
|
||||
title="${o("remove",e)}">✕</button>
|
||||
</div>`)}
|
||||
</div>`:f}
|
||||
${this._photos.full?l`<div class="photo-limit">${o("photos_limit",e).replace("{max}",String(this._photos.max))}</div>`:l`<ms-photo-picker .lang=${e} .busy=${this._photos.uploading} .remaining=${this._photos.remaining}
|
||||
@files-picked=${t=>this._photos.addFiles(t.detail.files)}
|
||||
></ms-photo-picker>`}
|
||||
</div>
|
||||
${this.adaptiveEnabled?l`
|
||||
<div class="feedback-section">
|
||||
<label class="feedback-label">${o("was_maintenance_needed",e)}</label>
|
||||
<div class="feedback-buttons">
|
||||
<button
|
||||
class="feedback-btn ${this._feedback==="needed"?"selected":""}"
|
||||
@click=${()=>this._setFeedback("needed")}
|
||||
>${o("feedback_needed",e)}</button>
|
||||
<button
|
||||
class="feedback-btn ${this._feedback==="not_needed"?"selected":""}"
|
||||
@click=${()=>this._setFeedback("not_needed")}
|
||||
>${o("feedback_not_needed",e)}</button>
|
||||
<button
|
||||
class="feedback-btn ${this._feedback==="not_sure"?"selected":""}"
|
||||
@click=${()=>this._setFeedback("not_sure")}
|
||||
>${o("feedback_not_sure",e)}</button>
|
||||
</div>
|
||||
</div>
|
||||
`:f}
|
||||
</div>
|
||||
<div class="dialog-actions">
|
||||
<ha-button appearance="plain" @click=${this._close}>
|
||||
${o("cancel",e)}
|
||||
</ha-button>
|
||||
<ha-button
|
||||
@click=${this._complete}
|
||||
.disabled=${this._loading||this._photos.uploading||this._missingRequired.length>0}
|
||||
title=${this._photos.uploading?o("uploading",e):this._missingRequired.length?this._missingRequired.map(t=>o("err_required",e).replace("{field}",o(B[t]??t,e))).join(" \xB7 "):""}
|
||||
>
|
||||
${this._loading?o("completing",e):o("complete",e)}
|
||||
</ha-button>
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`}};c.styles=[C,Z,x`
|
||||
.req-mark {
|
||||
color: var(--error-color, #f44336);
|
||||
margin-left: 2px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.notes-input { resize: vertical; line-height: 1.4; }
|
||||
/* #104: one-click cost suggestion from parts — quiet link-style chip. */
|
||||
.cost-note {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
margin-top: 4px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.cost-suggestion {
|
||||
align-self: flex-start;
|
||||
margin-top: 4px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--primary-color);
|
||||
font-size: 12.5px;
|
||||
cursor: pointer;
|
||||
text-decoration: underline dotted;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
.dialog-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.scan-required-note {
|
||||
margin: -4px 0 12px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 152, 0, 0.12);
|
||||
color: var(--primary-text-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
.phase-line {
|
||||
margin-top: -8px;
|
||||
padding-bottom: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
min-width: 300px;
|
||||
}
|
||||
.dialog-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
.consumes-hint {
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
border-left: 3px solid var(--primary-color);
|
||||
padding: 4px 8px;
|
||||
margin: 4px 0 8px;
|
||||
}
|
||||
/* #99: editable per-completion parts selection */
|
||||
.used-parts { margin: 4px 0 8px; display: flex; flex-direction: column; gap: 4px; }
|
||||
.used-part-row { display: flex; align-items: center; gap: 8px; }
|
||||
.used-part-check {
|
||||
display: flex; align-items: center; gap: 6px; flex: 1;
|
||||
font-size: 13px; cursor: pointer;
|
||||
}
|
||||
.used-part-check input { cursor: pointer; }
|
||||
/* #111: whose stock this row draws on. Muted but never omitted — an
|
||||
unlabelled foreign pool is indistinguishable from an own part. */
|
||||
.used-part-owner { color: var(--secondary-text-color); }
|
||||
.used-part-qty {
|
||||
width: 76px; padding: 4px 6px; border-radius: 4px; font: inherit; font-size: 13px;
|
||||
border: 1px solid var(--divider-color);
|
||||
background: var(--card-background-color);
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.error {
|
||||
color: var(--error-color, #f44336);
|
||||
font-size: 13px;
|
||||
}
|
||||
/* .field/.field-label/.field-input come from nativeFieldStyles */
|
||||
/* #161 phase 2: the per-slot reading fields */
|
||||
.readings-block { display: flex; flex-direction: column; gap: 8px; }
|
||||
.readings-block > .field-label { margin-bottom: -4px; }
|
||||
.reading-warn {
|
||||
font-size: 12px;
|
||||
color: var(--warning-color, #ff9800);
|
||||
}
|
||||
/* .photo-pick / .photo-pickers / .photo-android-hint come from photoPickerStyles */
|
||||
/* #163: the backdate moment starts EMPTY (= now); the button seeds the
|
||||
HA date+time picker with the current minute instead of the picker's
|
||||
own 00:00 default, so a backdated completion never lands at midnight
|
||||
by accident. */
|
||||
.backdate-pick {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border: 1px dashed var(--divider-color);
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
width: fit-content;
|
||||
--mdc-icon-size: 18px;
|
||||
}
|
||||
.backdate-pick:hover { border-color: var(--primary-color); }
|
||||
/* #161: several photos per completion — tiles wrap into a strip,
|
||||
the two pickers (camera / gallery) sit underneath while there is
|
||||
room left under the cap. */
|
||||
.photo-strip {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin: 8px 0; /* room for the remove badges above the tiles */
|
||||
}
|
||||
.photo-limit {
|
||||
font-size: 12px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.photo-preview {
|
||||
position: relative;
|
||||
width: fit-content;
|
||||
}
|
||||
/* Uniform tiles: a tiny or portrait shot must not collapse the strip. */
|
||||
.photo-preview img {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
object-fit: cover;
|
||||
border-radius: 8px;
|
||||
display: block;
|
||||
background: var(--secondary-background-color, rgba(0,0,0,0.06));
|
||||
}
|
||||
.photo-remove {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: var(--error-color, #db4437);
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
}
|
||||
.checklist-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.checklist-label {
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.checklist-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
padding: 4px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
.checklist-item input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.feedback-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 8px 0;
|
||||
border-top: 1px solid var(--divider-color);
|
||||
}
|
||||
.feedback-label {
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.feedback-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.feedback-btn {
|
||||
flex: 1;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 8px;
|
||||
background: var(--card-background-color, #fff);
|
||||
color: var(--primary-text-color);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.feedback-btn:hover {
|
||||
background: var(--secondary-background-color, #f5f5f5);
|
||||
}
|
||||
.feedback-btn.selected {
|
||||
background: var(--primary-color);
|
||||
color: var(--text-primary-color, #fff);
|
||||
border-color: var(--primary-color);
|
||||
}
|
||||
`],a([h({attribute:!1})],c.prototype,"hass",2),a([h()],c.prototype,"entryId",2),a([h()],c.prototype,"taskId",2),a([h()],c.prototype,"taskName",2),a([h()],c.prototype,"lang",2),a([h({type:Array})],c.prototype,"checklist",2),a([h({type:Boolean})],c.prototype,"adaptiveEnabled",2),a([h()],c.prototype,"taskType",2),a([h()],c.prototype,"readingUnit",2),a([h({attribute:!1})],c.prototype,"readings",2),a([h({attribute:!1})],c.prototype,"readingHistory",2),a([h({attribute:!1})],c.prototype,"restockDefault",2),a([h({attribute:!1})],c.prototype,"restockUnitCost",2),a([h({attribute:!1})],c.prototype,"restockPackage",2),a([h()],c.prototype,"currencySymbol",2),a([h({attribute:!1})],c.prototype,"partsCostMode",2),a([h({attribute:!1})],c.prototype,"parts",2),a([h({attribute:!1})],c.prototype,"consumesParts",2),a([h({type:Array})],c.prototype,"consumesInfo",2),a([h({type:Array})],c.prototype,"requiredFields",2),a([h()],c.prototype,"phaseLabel",2),a([h({type:Boolean})],c.prototype,"requireTagScan",2),a([h({type:Boolean})],c.prototype,"viaTagScan",2),a([_()],c.prototype,"_open",2),a([_()],c.prototype,"_notes",2),a([_()],c.prototype,"_cost",2),a([_()],c.prototype,"_duration",2),a([_()],c.prototype,"_loading",2),a([_()],c.prototype,"_error",2),a([_()],c.prototype,"_checklistState",2),a([_()],c.prototype,"_feedback",2),a([_()],c.prototype,"_readingValue",2),a([_()],c.prototype,"_readingValues",2),a([_()],c.prototype,"_restockQty",2),a([_()],c.prototype,"_completedAt",2),a([_()],c.prototype,"_usedParts",2),a([_()],c.prototype,"_usedQtyText",2),a([h({attribute:!1})],c.prototype,"checklistPrefill",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",c);export{S as a,Z as b,T as c,c as d};
|
||||
@@ -0,0 +1,2 @@
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
import{b as s}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-JQXHX4U4.js";async function a(o,n,t=300){return(await o.connection.sendMessagePromise({type:"auth/sign_path",path:n,expires:t})).path}async function r(o,n,t=300){return a(o,`/api/maintenance_supporter/document/${n}`,t)}async function p(o,n,t=""){let e=window.open("about:blank","_blank");try{let i=await r(o,n);e&&(e.location.href=new URL(i+t,window.location.origin).href)}catch(i){throw e&&e.close(),i}}async function l(o,n,t){s(await r(o,n,30),t)}function m(o,n=null){let t=URL.createObjectURL(new Blob([o],{type:"text/html"}));n&&!n.closed?n.location.href=t:window.open(t,"_blank"),setTimeout(()=>URL.revokeObjectURL(t),6e4)}function u(){return window.open("about:blank","_blank")}export{a,r as b,p as c,l as d,m as e,u as f};
|
||||
@@ -0,0 +1,2 @@
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
import{O as n,s as o}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3FGYBPXY.js";var l={"Completed from dashboard button":"hist_note_button","Completed from the To-do list":"hist_note_todo","Completed by voice":"hist_note_voice","Completed from the notification":"hist_note_notification","Completed via NFC tag":"hist_note_nfc","Completed from the shopping list":"hist_note_shopping_list","Completed from a mirrored to-do list":"hist_note_todo_mirror","Skipped from notification":"hist_note_skipped_notification","Skipped from dashboard button":"hist_note_skipped_button","Sensor trigger activated":"hist_note_sensor_triggered","Initial value set during task creation":"hist_note_initial"},m=[[/^Auto-completed: sensor recovered \((.+)\)$/,"hist_note_auto_recovered",t=>({value:t[1]})],[/^Reset to (\d{4}-\d{2}-\d{2})$/,"hist_note_reset",(t,e)=>({date:n(t[1],e)})],[/^Trigger entity replaced: (.+) → (.+)$/,"hist_note_trigger_replaced",t=>({old:t[1],new:t[2]})],[/^Sensor trigger removed \(entity was: (.+)\)\. Schedule converted to (\w+)\.$/,"hist_note_trigger_removed",(t,e)=>({entity:t[1],schedule:o(t[2],e)})],[/^Entity (.+) removed from compound trigger; (\d+) conditions remain\.$/,"hist_note_compound_removed",t=>({entity:t[1],count:t[2]})]];function h(t,e){if(!t)return"";let i=l[t.trim()];if(i)return o(i,e);for(let[s,d,_]of m){let r=t.trim().match(s);if(r){let c=_(r,e);return o(d,e).replace(/\{(\w+)\}/g,(a,g)=>c[g]??a)}}return t}export{h as a};
|
||||
@@ -0,0 +1,2 @@
|
||||
/*! maintenance_supporter frontend 2.99.0 */
|
||||
import{H as p,r as g,s as o,v}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-3FGYBPXY.js";var w={already_archived:"ws_err_already_archived",already_paused:"ws_err_already_paused",archived:"ws_err_archived",changed_since:"undo_changed",create_failed:"ws_err_create_failed",duplicate_failed:"ws_err_duplicate_failed",empty:"ws_err_empty",empty_csv:"ws_err_empty_csv",invalid_cursor:"ws_err_invalid_cursor",invalid_date:"ws_err_invalid_date",invalid_device:"ws_err_invalid_device",invalid_entity_slug:"ws_err_invalid_entity_slug",invalid_format:"ws_err_invalid_format",invalid_input:"ws_err_invalid_input",invalid_mirror_todo:"ws_err_invalid_mirror_todo",invalid_parent:"ws_err_invalid_parent",invalid_icon:"ws_err_invalid_icon",invalid_range:"ws_err_invalid_range",invalid_search_template:"ws_err_invalid_search_template",invalid_target:"ws_err_invalid_target",invalid_trigger_config:"ws_err_invalid_trigger_config",invalid_url:"ws_err_invalid_url",invalid_user:"ws_err_invalid_user",invalid_view:"ws_err_invalid_view",limit_reached:"ws_err_limit_reached",no_defaults:"ws_err_no_defaults",no_phases:"ws_err_no_phases",no_url:"ws_err_no_url",not_archived:"ws_err_not_archived",not_available:"ws_err_not_available",not_configured:"ws_err_not_configured",not_found:"ws_err_not_found",not_loaded:"ws_err_not_loaded",not_paused:"ws_err_not_paused",nothing_to_undo:"undo_nothing",replace_failed:"ws_err_replace_failed",self_link_device:"ws_err_self_link_device",storage_unavailable:"ws_err_storage_unavailable",too_early:"ws_err_too_early",too_large:"ws_err_too_large",too_many:"ws_err_too_many",too_many_views:"ws_err_too_many_views",unauthorized:"ws_err_unauthorized",unavailable:"ws_err_unavailable"},h={entry_id:"object",name:"name",task_type:"maintenance_type",schedule_type:"schedule_type",interval_days:"interval_days",interval_anchor:"interval_anchor",warning_days:"warning_days",last_performed:"last_performed_optional",notes:"notes_optional",documentation_url:"documentation_url_optional",custom_icon:"custom_icon_optional",nfc_tag_id:"nfc_tag_id_optional",responsible_user_id:"responsible_user",entity_slug:"entity_slug",entity_id:"entity_id",area_id:"area_id_optional",manufacturer:"manufacturer_optional",model:"model_optional",serial_number:"serial_number_optional",installation_date:"installation_date_optional",warranty_expiry:"warranty_expiry_optional",checklist:"checklist_steps_optional",reason:"reason",feedback:"feedback",cost:"cost",duration:"duration",description:"description_optional",environmental_entity:"environmental_entity_optional",environmental_attribute:"environmental_attribute_optional",trigger_above:"trigger_above",trigger_below:"trigger_below",trigger_equals:"trigger_equals",trigger_not_equals:"trigger_not_equals",trigger_for_minutes:"trigger_for_minutes"};function y(n){let t=n.replace(/_ids?$/,"").split("_").filter(Boolean);if(t.length===0)return n;let e=t.join(" ");return e.charAt(0).toUpperCase()+e.slice(1)}function b(n,t){let e=h[n];if(e){let r=o(e,t);if(r&&r!==e)return r}return y(n)}function k(n){let t=[...n.matchAll(/\['([^']+)'\]/g)].map(i=>i[1]),e=t.length?t[t.length-1]:void 0,r;return(r=n.match(/length of value must be at most (\d+)/))?{field:e,rule:"too_long",param:r[1]}:(r=n.match(/length of value must be at least (\d+)/))?{field:e,rule:"too_short",param:r[1]}:(r=n.match(/value must be at most (\S+)/))?{field:e,rule:"value_too_high",param:r[1]}:(r=n.match(/value must be at least (\S+)/))?{field:e,rule:"value_too_low",param:r[1]}:/required key not provided/.test(n)?{field:e,rule:"required"}:(r=n.match(/expected (\w+)/))?{field:e,rule:"wrong_type",param:r[1]}:/value must be one of/.test(n)?{field:e,rule:"invalid_choice"}:/not a valid value/.test(n)?{field:e,rule:"invalid_value"}:{field:e,rule:"unknown"}}var f=n=>n.toLowerCase().split(/[^a-z0-9]+/).filter(t=>t.length>1);function E(n,t){let e=new Set(f(t));return f(n).every(r=>e.has(r))}function c(n,t,e){if(e=e??o("action_error",t),typeof n=="string")return n;if(typeof n!="object"||n===null)return e;let r=n,i=r.message||r.error?.message||"",_=r.code||r.error?.code||"",l=_?w[_]:void 0,d=v(r.translation_domain??r.error?.translation_domain,r.translation_key??r.error?.translation_key,r.translation_placeholders??r.error?.translation_placeholders);if(d)return d;if(!i&&!l)return e;let s=k(i),m=s.field?b(s.field,t):"",a=u=>o(u,t).replace("{field}",m).replace("{n}",s.param??"");switch(s.rule){case"too_long":return a("err_too_long");case"too_short":return a("err_too_short");case"value_too_high":return a("err_value_too_high");case"value_too_low":return a("err_value_too_low");case"required":return a("err_required");case"wrong_type":return a("err_wrong_type").replace("{type}",s.param??"");case"invalid_choice":return a("err_invalid_choice");case"invalid_value":return a("err_invalid_value");default:if(l){let u=o(l,t);return g(t)==="en"&&i&&!E(i,_)?`${u} (${i})`:u}return i||e}}async function W(n,t,e={}){e.busy?.(!0);try{let r=typeof t=="function"?await t():await n.hass.coLine truncated
|
||||
Loaded 100 of 362 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user