393 files

This commit is contained in:
Home Assistant Version Control
2026-09-27 17:16:54 +00:00
parent b05b7a897e
commit 39d97ac2db
404 changed files with 54006 additions and 11034 deletions
@@ -15,6 +15,7 @@ from .const import (
CONF_TASK_ENDS_UNTIL,
CONF_TASK_INTERVAL_UNIT,
CONF_TASK_SEASON_MONTHS,
SCHEDULE_OFFSET_MAX_DAYS,
)
from .helpers.dates import INTERVAL_UNITS
from .helpers.entity_analyzer import EntityAnalyzer
@@ -29,6 +30,27 @@ from .helpers.threshold_calculator import ThresholdCalculator, ThresholdSuggesti
_LOGGER = logging.getLogger(__name__)
# Flow error key for a last-performed date after today (strings.json config +
# options ``error``).
ERROR_LAST_PERFORMED_FUTURE = "last_performed_future"
def is_future_date(value: Any) -> bool:
"""True when ``value`` is an ISO date AFTER today (HA's time zone).
A last-performed date is a day the work WAS done: one in the future (the
date picker reaches year 9999) overflowed the schedule math inside every
refresh and kept the object in setup-retry (bug audit 2026-09-27). A
malformed value is not "future" — the selectors already refuse it.
"""
from homeassistant.util import dt as dt_util
from .helpers.dates import parse_iso_date
parsed = parse_iso_date(str(value)) if value else None
return parsed is not None and parsed > dt_util.now().date()
# Calendar recurrence kinds offered in the config + options flows (Phase 4;
# the calendar-entity kind since #187). Hardcoded English labels for the
# weekday/occurrence sub-options keep the config-flow i18n surface small; the
@@ -103,7 +125,9 @@ def calendar_schema(kind: str, current: dict[str, Any] | None = None) -> vol.Sch
# (#83) ±N-day shift of the computed occurrence, on every calendar kind
# ("two days before the last working day" = last_day + business + offset -2).
fields[vol.Optional("offset", default=cur.get("offset", 0))] = selector.NumberSelector(
selector.NumberSelectorConfig(min=-15, max=15, step=1, mode=selector.NumberSelectorMode.BOX)
selector.NumberSelectorConfig(
min=-SCHEDULE_OFFSET_MAX_DAYS, max=SCHEDULE_OFFSET_MAX_DAYS, step=1, mode=selector.NumberSelectorMode.BOX
)
)
return vol.Schema(fields)
@@ -159,6 +183,23 @@ def calendar_current(task: dict[str, Any]) -> dict[str, Any]:
}
def select_default(value: Any, options: list[Any], fallback: Any = "") -> Any:
"""A stored value made safe as a ``SelectSelector`` default.
HA validates the submitted form against the select's options, and a
stored value the options no longer carry — a deleted user, a user
promoted to admin (no longer on the non-admin list), the ``None`` the
panel dialog persisted for "no rotation" — came back as the field's
default and failed that validation on every save: the whole form was
unsaveable (bug audit 2026-09-26). A multi-select keeps only its still
valid members; a single select falls back to ``fallback``.
"""
allowed = {opt["value"] if isinstance(opt, dict) else opt for opt in options}
if isinstance(value, (list, tuple)):
return [v for v in value if v in allowed]
return value if value in allowed else fallback
def interval_unit_selector() -> selector.SelectSelector:
"""Shared days/weeks/months/years dropdown for the interval unit.