278 files

This commit is contained in:
Home Assistant Version Control committed 2026-09-13 20:38:58 +00:00
1 parent d7a0a08372
commit 1b4f5f68c6
278 files changed
+36892 -7150

No files matched your search

@@ -2,7 +2,6 @@
from __future__ import annotations
from datetime import date
from typing import Any
import voluptuous as vol
@@ -11,7 +10,6 @@ from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util
from ..const import (
CONF_OBJECT,
CONF_VACATION_BUFFER_DAYS,
CONF_VACATION_ENABLED,
CONF_VACATION_END,
@@ -20,10 +18,13 @@ from ..const import (
DEFAULT_WARNING_DAYS,
DOMAIN,
MAX_ID_LENGTH,
MAX_VACATION_EXEMPT_TASKS,
)
from ..helpers.aggregate import object_name
from ..helpers.dates import parse_iso_date
from ..helpers.schedule import read_legacy_fields
from ..helpers.vacation import compute_preview, get_vacation_state
from . import _get_merged_tasks, _get_object_entries, _load_global_options, _save_global_options
from . import _get_merged_tasks, _get_object_entries, _load_global_options, _parse_iso_date, _save_global_options
def _state_payload(hass: HomeAssistant) -> dict[str, Any]:
@@ -51,7 +52,7 @@ async def ws_vacation_state(
vol.Optional("buffer_days"): vol.All(int, vol.Range(min=0, max=14)),
vol.Optional("exempt_task_ids"): vol.All(
[vol.All(str, vol.Length(max=MAX_ID_LENGTH))],
vol.Length(max=2000),
vol.Length(max=MAX_VACATION_EXEMPT_TASKS),
),
}
)
@@ -71,38 +72,21 @@ async def ws_vacation_update(
if "enabled" in msg:
options[CONF_VACATION_ENABLED] = bool(msg["enabled"])
if "start" in msg:
if msg["start"] is None:
options[CONF_VACATION_START] = None
else:
try:
date.fromisoformat(msg["start"])
except (TypeError, ValueError):
connection.send_error(msg["id"], "invalid_date", "start must be YYYY-MM-DD")
return
options[CONF_VACATION_START] = msg["start"]
for field, key in (("start", CONF_VACATION_START), ("end", CONF_VACATION_END)):
if field not in msg:
continue
if msg[field] is not None and _parse_iso_date(connection, msg["id"], msg[field], field=field) is None:
return
options[key] = msg[field]
if "end" in msg:
if msg["end"] is None:
options[CONF_VACATION_END] = None
else:
try:
date.fromisoformat(msg["end"])
except (TypeError, ValueError):
connection.send_error(msg["id"], "invalid_date", "end must be YYYY-MM-DD")
return
options[CONF_VACATION_END] = msg["end"]
# End-vs-start sanity (only when both are present after the patch).
sd = options.get(CONF_VACATION_START)
ed = options.get(CONF_VACATION_END)
if sd and ed:
try:
if date.fromisoformat(ed) < date.fromisoformat(sd):
connection.send_error(msg["id"], "invalid_range", "end must be on or after start")
return
except (TypeError, ValueError):
pass # Already rejected above
# End-vs-start sanity (only when both are present after the patch; a
# stored value that does not parse is skipped — the patch above already
# refused a malformed incoming one).
sd = parse_iso_date(options.get(CONF_VACATION_START))
ed = parse_iso_date(options.get(CONF_VACATION_END))
if sd is not None and ed is not None and ed < sd:
connection.send_error(msg["id"], "invalid_range", "end must be on or after start")
return
if "buffer_days" in msg:
options[CONF_VACATION_BUFFER_DAYS] = int(msg["buffer_days"])
@@ -119,7 +103,7 @@ async def ws_vacation_update(
continue
seen.add(v)
cleaned.append(v)
if len(cleaned) >= 2000:
if len(cleaned) >= MAX_VACATION_EXEMPT_TASKS:
break
options[CONF_VACATION_EXEMPT_TASK_IDS] = cleaned
@@ -152,8 +136,7 @@ async def ws_vacation_preview(
# Build the flat task list expected by compute_preview.
tasks: list[dict[str, Any]] = []
for entry in _get_object_entries(hass):
obj_data = entry.data.get(CONF_OBJECT, {})
obj_name = obj_data.get("name", "")
obj_name = object_name(entry)
# Merge dynamic store fields (last_performed, etc.) when available.
merged = _get_merged_tasks(entry)
@@ -210,13 +193,9 @@ async def ws_vacation_end_now(
# the user actually returned. Use HA's configured timezone — the user's
# "today" should match what their dashboard shows, not the server's UTC.
today = dt_util.now().date()
sd = options.get(CONF_VACATION_START)
if sd:
try:
if date.fromisoformat(sd) <= today:
options[CONF_VACATION_END] = today.isoformat()
except (TypeError, ValueError):
pass
sd = parse_iso_date(options.get(CONF_VACATION_START))
if sd is not None and sd <= today:
options[CONF_VACATION_END] = today.isoformat()
_save_global_options(hass, global_entry, options)
connection.send_result(msg["id"], _state_payload(hass))