mirror of
https://github.com/smkrv/ha-text-ai.git
synced 2026-07-21 22:54:00 +08:00
fix: Services survive config entry reload, get_history returns a dict response
- service registration extracted to idempotent _async_register_services,
called from both async_setup and async_setup_entry: unloading the last
entry unregisters services, and a reload (every options change) never
re-ran async_setup, leaving the integration without services
- get_history handler wraps the list in {"history": [...]}: HA rejects
non-dict action responses, so every return_response call failed with a
server error since the service gained SupportsResponse (v2.4.x) - the
path never worked, no consumer could depend on the old shape
- README: get_history example shows response_variable usage and the
actual limit clamp semantics
Both found by live smoke test in HA 2026.7 (Docker), not by static review
This commit is contained in:
@@ -116,6 +116,19 @@ async def async_setup(hass: HomeAssistant, config: dict[str, Any]) -> bool:
|
||||
"""Set up the Home Assistant Text AI component."""
|
||||
# Initialize domain data storage
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
_async_register_services(hass)
|
||||
return True
|
||||
|
||||
def _async_register_services(hass: HomeAssistant) -> None:
|
||||
"""Register domain services; safe to call again after unload.
|
||||
|
||||
Unloading the last config entry unregisters the services, and a config
|
||||
entry reload (every options change does one) runs unload + setup_entry
|
||||
without re-running async_setup — so setup_entry must be able to bring
|
||||
the services back.
|
||||
"""
|
||||
if hass.services.has_service(DOMAIN, SERVICE_ASK_QUESTION):
|
||||
return
|
||||
|
||||
async def async_ask_question(call: ServiceCall) -> dict:
|
||||
"""Handle ask_question service with response data."""
|
||||
@@ -171,17 +184,21 @@ async def async_setup(hass: HomeAssistant, config: dict[str, Any]) -> bool:
|
||||
_LOGGER.error("Error clearing history: %s", str(err))
|
||||
raise HomeAssistantError(f"Failed to clear history: {str(err)}") from err
|
||||
|
||||
async def async_get_history(call: ServiceCall) -> list:
|
||||
async def async_get_history(call: ServiceCall) -> dict:
|
||||
"""Handle get_history service."""
|
||||
try:
|
||||
coordinator = get_coordinator_by_instance(hass, call.data["instance"])
|
||||
return await coordinator.async_get_history(
|
||||
history = await coordinator.async_get_history(
|
||||
limit=call.data.get("limit"),
|
||||
filter_model=call.data.get("filter_model"),
|
||||
start_date=call.data.get("start_date"),
|
||||
include_metadata=call.data.get("include_metadata", False),
|
||||
sort_order=call.data.get("sort_order", "newest")
|
||||
)
|
||||
# HA requires action responses to be dicts. The bare list made
|
||||
# every return_response call fail with a server error, so this
|
||||
# path never worked before and the wrapper breaks no consumer.
|
||||
return {"history": history}
|
||||
except Exception as err:
|
||||
_LOGGER.error("Error getting history: %s", str(err))
|
||||
raise HomeAssistantError(f"Failed to get history: {str(err)}") from err
|
||||
@@ -226,8 +243,6 @@ async def async_setup(hass: HomeAssistant, config: dict[str, Any]) -> bool:
|
||||
schema=SERVICE_SCHEMA_SET_SYSTEM_PROMPT
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
async def async_check_api(session, endpoint: str, headers: dict, provider: str, api_timeout: int = DEFAULT_API_TIMEOUT) -> bool:
|
||||
"""Check API availability using provider registry configuration."""
|
||||
try:
|
||||
@@ -353,6 +368,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = coordinator
|
||||
|
||||
# A reload after the last entry was unloaded needs the services back.
|
||||
_async_register_services(hass)
|
||||
|
||||
_LOGGER.debug("Stored coordinator in hass.data[%s][%s]", DOMAIN, entry.entry_id)
|
||||
|
||||
# Set up platforms
|
||||
|
||||
Reference in New Issue
Block a user