hass.tibber_prices/custom_components/tibber_prices/services/refresh_user_data.py
Julian Pawlowski 6e0613c055 feat(services): add 5 scheduling services for price-optimized time windows
New services for finding optimal electricity price windows:
- find_cheapest_block: Cheapest contiguous time block (e.g., dishwasher)
- find_cheapest_hours: Cheapest N hours, non-contiguous (e.g., EV charging)
- find_cheapest_schedule: Multi-task scheduling with no-overlap (e.g., shared circuit)
- find_most_expensive_block: Most expensive contiguous block (peak avoidance)
- find_most_expensive_hours: Most expensive N hours (consumption shifting)

Key features:
- Flexible search range (today, tomorrow, today+tomorrow, rolling window)
- Power profile support for variable consumption patterns
- Price level filtering (e.g., only CHEAP/VERY_CHEAP intervals)
- Comparison details showing savings vs. alternatives
- Sliding window algorithm (O(n)) for block search, greedy scheduling
  for multi-task optimization

Also includes:
- Shared validation utilities (search range, price level, power profile)
- entry_id now optional on all services (auto-selects single home)
- Input validation for existing services (time range, filter conflicts)
- Service icons for all new and existing services
- Translations for all 5 languages (en, de, nb, nl, sv)
- Removed 10 unused config.error translation keys (replaced by exceptions)
- Tests for price window algorithms and search range resolution

Impact: Users can find optimal time windows for appliances, EV charging,
and multi-device scheduling via HA service calls. Existing services
improved with optional entry_id and better input validation.
2026-04-11 18:58:27 +00:00

107 lines
2.9 KiB
Python

"""
User data refresh service handler.
This module implements the `refresh_user_data` service, which forces a refresh
of user profile and home information from the Tibber API.
Features:
- Force refresh of cached user data
- Bypass 24h cache TTL
- Return updated user profile and homes
- Error handling for API failures
Service: tibber_prices.refresh_user_data
Response: JSON with refresh status and updated data
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Final
import voluptuous as vol
from custom_components.tibber_prices.api import (
TibberPricesApiClientAuthenticationError,
TibberPricesApiClientCommunicationError,
TibberPricesApiClientError,
)
from homeassistant.exceptions import ServiceValidationError
from .helpers import get_entry_and_data
if TYPE_CHECKING:
from homeassistant.core import ServiceCall
# Service constants
REFRESH_USER_DATA_SERVICE_NAME: Final = "refresh_user_data"
ATTR_ENTRY_ID: Final = "entry_id"
# Service schema
REFRESH_USER_DATA_SERVICE_SCHEMA: Final = vol.Schema(
{
vol.Optional(ATTR_ENTRY_ID, default=""): str,
}
)
async def handle_refresh_user_data(call: ServiceCall) -> dict[str, Any]:
"""
Refresh user data for a specific config entry.
Forces a refresh of user profile and home information from Tibber API,
bypassing the 24-hour cache TTL. Returns updated information or error details.
See services.yaml for detailed parameter documentation.
Args:
call: Service call with parameters
Returns:
Dictionary with refresh status and updated data
Raises:
ServiceValidationError: If entry_id is missing or invalid
"""
entry_id = call.data.get(ATTR_ENTRY_ID, "")
hass = call.hass
# Get the entry and coordinator
try:
_, coordinator, _ = get_entry_and_data(hass, entry_id)
except ServiceValidationError as ex:
return {
"success": False,
"message": f"Invalid entry ID: {ex}",
}
# Force refresh user data using the public method
try:
updated = await coordinator.refresh_user_data()
except (
TibberPricesApiClientAuthenticationError,
TibberPricesApiClientCommunicationError,
TibberPricesApiClientError,
) as ex:
return {
"success": False,
"message": f"API error refreshing user data: {ex!s}",
}
else:
if updated:
user_profile = coordinator.get_user_profile()
homes = coordinator.get_user_homes()
return {
"success": True,
"message": "User data refreshed successfully",
"user_profile": user_profile,
"homes_count": len(homes),
"homes": homes,
"last_updated": user_profile.get("last_updated"),
}
return {
"success": False,
"message": "User data was already up to date",
}