Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CODEOWNERS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion homeassistant/components/abode/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import voluptuous as vol

from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.dispatcher import dispatcher_send

Expand Down Expand Up @@ -70,6 +70,7 @@ def _trigger_automation(call: ServiceCall) -> None:
dispatcher_send(call.hass, signal)


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Home Assistant services."""

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/alexa_devices/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "cloud_polling",
"loggers": ["aioamazondevices"],
"quality_scale": "bronze",
"requirements": ["aioamazondevices==3.0.6"]
"requirements": ["aioamazondevices==3.1.2"]
}
3 changes: 2 additions & 1 deletion homeassistant/components/amcrest/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from homeassistant.auth.models import User
from homeassistant.auth.permissions.const import POLICY_CONTROL
from homeassistant.const import ATTR_ENTITY_ID, ENTITY_MATCH_ALL, ENTITY_MATCH_NONE
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import Unauthorized, UnknownUser
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.service import async_extract_entity_ids
Expand All @@ -15,6 +15,7 @@
from .helpers import service_signal


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Set up the Amcrest IP Camera services."""

Expand Down
33 changes: 21 additions & 12 deletions homeassistant/components/anthropic/conversation.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,26 @@ async def _async_handle_message(
except conversation.ConverseError as err:
return err.as_conversation_result()

await self._async_handle_chat_log(chat_log)

response_content = chat_log.content[-1]
if not isinstance(response_content, conversation.AssistantContent):
raise TypeError("Last message must be an assistant message")
intent_response = intent.IntentResponse(language=user_input.language)
intent_response.async_set_speech(response_content.content or "")
return conversation.ConversationResult(
response=intent_response,
conversation_id=chat_log.conversation_id,
continue_conversation=chat_log.continue_conversation,
)

async def _async_handle_chat_log(
self,
chat_log: conversation.ChatLog,
) -> None:
"""Generate an answer for the chat log."""
options = self.entry.options

tools: list[ToolParam] | None = None
if chat_log.llm_api:
tools = [
Expand Down Expand Up @@ -424,7 +444,7 @@ async def _async_handle_message(
[
content
async for content in chat_log.async_add_delta_content_stream(
user_input.agent_id,
self.entity_id,
_transform_stream(chat_log, stream, messages),
)
if not isinstance(content, conversation.AssistantContent)
Expand All @@ -435,17 +455,6 @@ async def _async_handle_message(
if not chat_log.unresponded_tool_results:
break

response_content = chat_log.content[-1]
if not isinstance(response_content, conversation.AssistantContent):
raise TypeError("Last message must be an assistant message")
intent_response = intent.IntentResponse(language=user_input.language)
intent_response.async_set_speech(response_content.content or "")
return conversation.ConversationResult(
response=intent_response,
conversation_id=chat_log.conversation_id,
continue_conversation=chat_log.continue_conversation,
)

async def _async_entry_update_listener(
self, hass: HomeAssistant, entry: ConfigEntry
) -> None:
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/downloader/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import requests
import voluptuous as vol

from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.service import async_register_admin_service
from homeassistant.util import raise_if_invalid_filename, raise_if_invalid_path
Expand Down Expand Up @@ -141,6 +141,7 @@ def do_download() -> None:
threading.Thread(target=do_download).start()


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Register the services for the downloader component."""
async_register_admin_service(
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/elkm1/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def _set_time_service(service: ServiceCall) -> None:
_async_get_elk_panel(service).set_time(dt_util.now())


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Create ElkM1 services."""

Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/ffmpeg/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import voluptuous as vol

from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_send

Expand Down Expand Up @@ -35,6 +35,7 @@ async def _async_service_handle(service: ServiceCall) -> None:
async_dispatcher_send(service.hass, SIGNAL_FFMPEG_RESTART, entity_ids)


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Register FFmpeg services."""

Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/google_assistant_sdk/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
ServiceCall,
ServiceResponse,
SupportsResponse,
callback,
)
from homeassistant.helpers import config_validation as cv

Expand Down Expand Up @@ -49,6 +50,7 @@ async def _send_text_command(call: ServiceCall) -> ServiceResponse:
return None


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Add the services for Google Assistant SDK."""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,11 @@ def _create_google_tool_response_content(


def _convert_content(
content: conversation.UserContent
| conversation.AssistantContent
| conversation.SystemContent,
content: (
conversation.UserContent
| conversation.AssistantContent
| conversation.SystemContent
),
) -> Content:
"""Convert HA content to Google content."""
if content.role != "assistant" or not content.tool_calls:
Expand Down Expand Up @@ -381,6 +383,29 @@ async def _async_handle_message(
except conversation.ConverseError as err:
return err.as_conversation_result()

await self._async_handle_chat_log(chat_log)

response = intent.IntentResponse(language=user_input.language)
if not isinstance(chat_log.content[-1], conversation.AssistantContent):
LOGGER.error(
"Last content in chat log is not an AssistantContent: %s. This could be due to the model not returning a valid response",
chat_log.content[-1],
)
raise HomeAssistantError(f"{ERROR_GETTING_RESPONSE}")
response.async_set_speech(chat_log.content[-1].content or "")
return conversation.ConversationResult(
response=response,
conversation_id=chat_log.conversation_id,
continue_conversation=chat_log.continue_conversation,
)

async def _async_handle_chat_log(
self,
chat_log: conversation.ChatLog,
) -> None:
"""Generate an answer for the chat log."""
options = self.entry.options

tools: list[Tool | Callable[..., Any]] | None = None
if chat_log.llm_api:
tools = [
Expand Down Expand Up @@ -499,7 +524,9 @@ async def _async_handle_message(
chat = self._genai_client.aio.chats.create(
model=model_name, history=messages, config=generateContentConfig
)
chat_request: str | list[Part] = user_input.text
user_message = chat_log.content[-1]
assert isinstance(user_message, conversation.UserContent)
chat_request: str | list[Part] = user_message.content
# To prevent infinite loops, we limit the number of iterations
for _iteration in range(MAX_TOOL_ITERATIONS):
try:
Expand All @@ -519,7 +546,7 @@ async def _async_handle_message(
[
content
async for content in chat_log.async_add_delta_content_stream(
user_input.agent_id,
self.entity_id,
_transform_stream(chat_response_generator),
)
if isinstance(content, conversation.ToolResultContent)
Expand All @@ -529,20 +556,6 @@ async def _async_handle_message(
if not chat_log.unresponded_tool_results:
break

response = intent.IntentResponse(language=user_input.language)
if not isinstance(chat_log.content[-1], conversation.AssistantContent):
LOGGER.error(
"Last content in chat log is not an AssistantContent: %s. This could be due to the model not returning a valid response",
chat_log.content[-1],
)
raise HomeAssistantError(f"{ERROR_GETTING_RESPONSE}")
response.async_set_speech(chat_log.content[-1].content or "")
return conversation.ConversationResult(
response=response,
conversation_id=chat_log.conversation_id,
continue_conversation=chat_log.continue_conversation,
)

async def _async_entry_update_listener(
self, hass: HomeAssistant, entry: ConfigEntry
) -> None:
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/google_photos/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
ServiceCall,
ServiceResponse,
SupportsResponse,
callback,
)
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv
Expand Down Expand Up @@ -77,6 +78,7 @@ def _read_file_contents(
return results


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Register Google Photos services."""

Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/google_sheets/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import voluptuous as vol

from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.selector import ConfigEntrySelector
Expand Down Expand Up @@ -76,6 +76,7 @@ async def _async_append_to_sheet(call: ServiceCall) -> None:
await call.hass.async_add_executor_job(_append_to_sheet, call, entry)


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Add the services for Google Sheets."""

Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/habitica/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
ServiceCall,
ServiceResponse,
SupportsResponse,
callback,
)
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
from homeassistant.helpers import config_validation as cv
Expand Down Expand Up @@ -249,6 +250,7 @@ def get_config_entry(hass: HomeAssistant, entry_id: str) -> HabiticaConfigEntry:
return entry


@callback
def async_setup_services(hass: HomeAssistant) -> None: # noqa: C901
"""Set up services for Habitica integration."""

Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/hue/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from aiohue import HueBridgeV1, HueBridgeV2
import voluptuous as vol

from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.service import verify_domain_control

Expand All @@ -25,6 +25,7 @@
LOGGER = logging.getLogger(__name__)


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Register services for Hue integration."""

Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/icloud/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import voluptuous as vol

from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.core import HomeAssistant, ServiceCall, callback
from homeassistant.helpers import config_validation as cv
from homeassistant.util import slugify

Expand Down Expand Up @@ -115,6 +115,7 @@ def _get_account(hass: HomeAssistant, account_identifier: str) -> IcloudAccount:
return icloud_account


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Register iCloud services."""

Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/jewish_calendar/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
ServiceCall,
ServiceResponse,
SupportsResponse,
callback,
)
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
Expand All @@ -39,6 +40,7 @@
)


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Set up the Jewish Calendar services."""

Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/lcn/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
ServiceCall,
ServiceResponse,
SupportsResponse,
callback,
)
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv, device_registry as dr
Expand Down Expand Up @@ -438,6 +439,7 @@ class LcnService(StrEnum):
)


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Register services for LCN."""
for service_name, service in SERVICES:
Expand Down
5 changes: 0 additions & 5 deletions homeassistant/components/local_calendar/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@

PRODID = "-//homeassistant.io//local_calendar 1.0//EN"

# The calendar on disk is only changed when this entity is updated, so there
# is no need to poll for changes. The calendar enttiy base class will handle
# refreshing the entity state based on the start or end time of the event.
SCAN_INTERVAL = timedelta(days=1)


async def async_setup_entry(
hass: HomeAssistant,
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/nordpool/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
ServiceCall,
ServiceResponse,
SupportsResponse,
callback,
)
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv
Expand Down Expand Up @@ -66,6 +67,7 @@ def get_config_entry(hass: HomeAssistant, entry_id: str) -> NordPoolConfigEntry:
return entry


@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""Set up services for Nord Pool integration."""

Expand Down
Loading
Loading