182 files

This commit is contained in:
Home Assistant Version Control
2026-08-17 12:11:35 +00:00
parent 7dddf6bb13
commit ece15a1c1b
183 changed files with 11457 additions and 3092 deletions
@@ -92,6 +92,8 @@ from . import (
_get_global_entry,
_get_object_entries,
_get_runtime_data,
_load_global_options,
_save_global_options,
)
_LOGGER = logging.getLogger(__name__)
@@ -551,30 +553,13 @@ async def ws_get_budget_status(
# ---------------------------------------------------------------------------
@websocket_api.websocket_command(
{
vol.Required("type"): f"{DOMAIN}/global/update",
vol.Required("settings"): dict,
}
)
@websocket_api.require_admin
@websocket_api.async_response
async def ws_update_global_settings(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Update global settings.
def sanitize_settings_input(settings_input: dict[str, Any]) -> tuple[dict[str, Any], str | None]:
"""Filter + validate a flat settings dict against the registry.
Accepts a flat dict of setting keys to update. Unknown keys are
silently ignored. Returns the full updated settings.
"""
global_entry = _get_global_entry(hass)
if global_entry is None:
connection.send_error(msg["id"], "not_found", "Global config entry not found")
return
settings_input: dict[str, Any] = msg["settings"]
Returns ``(filtered, notify_error)`` — the validated subset plus the
error code when ``notify_service`` was present but invalid. Shared by
``global/update`` and the settings-export import path so the two
surfaces cannot drift."""
# Filter to allowed keys and validate types
filtered: dict[str, Any] = {}
@@ -692,24 +677,56 @@ async def ws_update_global_settings(
tids.append(v)
filtered[CONF_DISABLED_TEMPLATE_IDS] = tids
if not filtered:
connection.send_error(msg["id"], "invalid_input", "No valid setting keys provided")
return
# Validate notify_service if provided
if CONF_NOTIFY_SERVICE in filtered:
from ..config_flow_options_global import validate_notify_service
normalized, error = validate_notify_service(filtered[CONF_NOTIFY_SERVICE])
if error:
connection.send_error(msg["id"], error, f"Invalid notify service: {error}")
return
return filtered, error
filtered[CONF_NOTIFY_SERVICE] = normalized
return filtered, None
@websocket_api.websocket_command(
{
vol.Required("type"): f"{DOMAIN}/global/update",
vol.Required("settings"): dict,
}
)
@websocket_api.require_admin
@websocket_api.async_response
async def ws_update_global_settings(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Update global settings.
Accepts a flat dict of setting keys to update. Unknown keys are
silently ignored. Returns the full updated settings.
"""
ctx = _load_global_options(hass, connection, msg)
if ctx is None:
return
global_entry, current_options = ctx
settings_input: dict[str, Any] = msg["settings"]
filtered, notify_error = sanitize_settings_input(settings_input)
if notify_error:
connection.send_error(msg["id"], notify_error, f"Invalid notify service: {notify_error}")
return
if not filtered:
connection.send_error(msg["id"], "invalid_input", "No valid setting keys provided")
return
# Merge with existing options
merged = dict(global_entry.options or global_entry.data)
merged = current_options
merged.update(filtered)
hass.config_entries.async_update_entry(global_entry, options=merged)
_save_global_options(hass, global_entry, merged)
_LOGGER.debug("Global settings updated via WS: %s", list(filtered.keys()))
@@ -751,12 +768,10 @@ async def ws_test_notification(
send_test_notification,
)
global_entry = _get_global_entry(hass)
if global_entry is None:
connection.send_error(msg["id"], "not_found", "Global config entry not found")
ctx = _load_global_options(hass, connection, msg)
if ctx is None:
return
options = dict(global_entry.options or global_entry.data)
_global_entry, options = ctx
result_key = await send_test_notification(hass, options, user_id=msg.get("user_id"))
connection.send_result(
msg["id"],