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
2 changes: 2 additions & 0 deletions homeassistant/components/airtouch5/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ async def async_step_user(
_LOGGER.exception("Unexpected exception")
errors = {"base": "cannot_connect"}
else:
# Uses the host/IP value from CONF_HOST as unique ID, which is no longer allowed
# pylint: disable-next=hass-unique-id-ip-based
await self.async_set_unique_id(user_input[CONF_HOST])
self._abort_if_unique_id_configured()
return self.async_create_entry(
Expand Down
69 changes: 33 additions & 36 deletions homeassistant/components/anthropic/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,15 +703,14 @@ def __init__(self, entry: AnthropicConfigEntry, subentry: ConfigSubentry) -> Non
entry_type=dr.DeviceEntryType.SERVICE,
)

async def _async_handle_chat_log( # noqa: C901
async def _get_model_args( # noqa: C901
self,
chat_log: conversation.ChatLog,
structure_name: str | None = None,
structure: vol.Schema | None = None,
max_iterations: int = MAX_TOOL_ITERATIONS,
) -> None:
"""Generate an answer for the chat log."""
options = self.subentry.data
) -> tuple[MessageCreateParamsStreaming, str | None]:
"""Get the model arguments."""
options: dict[str, Any] = DEFAULT | self.subentry.data

preloaded_tools = [
"HassTurnOn",
Expand All @@ -729,41 +728,33 @@ async def _async_handle_chat_log( # noqa: C901

messages, container_id = _convert_content(chat_log.content[1:])

model = options.get(CONF_CHAT_MODEL, DEFAULT[CONF_CHAT_MODEL])
model = options[CONF_CHAT_MODEL]

model_args = MessageCreateParamsStreaming(
model=model,
messages=messages,
max_tokens=options.get(CONF_MAX_TOKENS, DEFAULT[CONF_MAX_TOKENS]),
max_tokens=options[CONF_MAX_TOKENS],
system=system.content,
stream=True,
container=container_id,
)

if (
options.get(CONF_PROMPT_CACHING, DEFAULT[CONF_PROMPT_CACHING])
== PromptCaching.PROMPT
):
if options[CONF_PROMPT_CACHING] == PromptCaching.PROMPT:
model_args["system"] = [
{
"type": "text",
"text": system.content,
"cache_control": {"type": "ephemeral"},
}
]
elif (
options.get(CONF_PROMPT_CACHING, DEFAULT[CONF_PROMPT_CACHING])
== PromptCaching.AUTOMATIC
):
elif options[CONF_PROMPT_CACHING] == PromptCaching.AUTOMATIC:
model_args["cache_control"] = {"type": "ephemeral"}

if (
self.model_info.capabilities
and self.model_info.capabilities.thinking.types.adaptive.supported
):
thinking_effort = options.get(
CONF_THINKING_EFFORT, DEFAULT[CONF_THINKING_EFFORT]
)
thinking_effort = options[CONF_THINKING_EFFORT]
if thinking_effort != "none":
model_args["thinking"] = ThinkingConfigAdaptiveParam(
type="adaptive", display="summarized"
Expand All @@ -772,9 +763,7 @@ async def _async_handle_chat_log( # noqa: C901
else:
model_args["thinking"] = ThinkingConfigDisabledParam(type="disabled")
else:
thinking_budget = options.get(
CONF_THINKING_BUDGET, DEFAULT[CONF_THINKING_BUDGET]
)
thinking_budget = options[CONF_THINKING_BUDGET]
if (
self.model_info.capabilities
and self.model_info.capabilities.thinking.types.enabled.supported
Expand All @@ -791,9 +780,7 @@ async def _async_handle_chat_log( # noqa: C901
and self.model_info.capabilities.effort.supported
):
model_args["output_config"] = OutputConfigParam(
effort=options.get(
CONF_THINKING_EFFORT, DEFAULT[CONF_THINKING_EFFORT]
)
effort=options[CONF_THINKING_EFFORT]
)

tools: list[ToolUnionParam] = []
Expand All @@ -803,12 +790,12 @@ async def _async_handle_chat_log( # noqa: C901
for tool in chat_log.llm_api.tools
]

if options.get(CONF_CODE_EXECUTION):
if options[CONF_CODE_EXECUTION]:
# The `web_search_20260209` tool automatically enables `code_execution_20260120` tool
if (
not self.model_info.capabilities
or not self.model_info.capabilities.code_execution.supported
or not options.get(CONF_WEB_SEARCH)
or not options[CONF_WEB_SEARCH]
):
tools.append(
CodeExecutionTool20250825Param(
Expand All @@ -817,26 +804,26 @@ async def _async_handle_chat_log( # noqa: C901
),
)

if options.get(CONF_WEB_SEARCH):
if options[CONF_WEB_SEARCH]:
if (
not self.model_info.capabilities
or not self.model_info.capabilities.code_execution.supported
or not options.get(CONF_CODE_EXECUTION)
or not options[CONF_CODE_EXECUTION]
):
web_search: WebSearchTool20250305Param | WebSearchTool20260209Param = (
WebSearchTool20250305Param(
name="web_search",
type="web_search_20250305",
max_uses=options.get(CONF_WEB_SEARCH_MAX_USES),
max_uses=options[CONF_WEB_SEARCH_MAX_USES],
)
)
else:
web_search = WebSearchTool20260209Param(
name="web_search",
type="web_search_20260209",
max_uses=options.get(CONF_WEB_SEARCH_MAX_USES),
max_uses=options[CONF_WEB_SEARCH_MAX_USES],
)
if options.get(CONF_WEB_SEARCH_USER_LOCATION):
if options[CONF_WEB_SEARCH_USER_LOCATION]:
web_search["user_location"] = {
"type": "approximate",
"city": options.get(CONF_WEB_SEARCH_CITY, ""),
Expand Down Expand Up @@ -937,10 +924,7 @@ async def _async_handle_chat_log( # noqa: C901
preloaded_tools.append(structure_name)

if tools:
if (
options.get(CONF_TOOL_SEARCH, DEFAULT[CONF_TOOL_SEARCH])
and len(tools) > len(preloaded_tools) + 1
):
if options[CONF_TOOL_SEARCH] and len(tools) > len(preloaded_tools) + 1:
for tool in tools:
if not tool["name"].endswith(tuple(preloaded_tools)):
tool["defer_loading"] = True
Expand All @@ -953,6 +937,19 @@ async def _async_handle_chat_log( # noqa: C901

model_args["tools"] = tools

return model_args, structure_name

async def _async_handle_chat_log(
self,
chat_log: conversation.ChatLog,
structure_name: str | None = None,
structure: vol.Schema | None = None,
max_iterations: int = MAX_TOOL_ITERATIONS,
) -> None:
"""Generate an answer for the chat log."""
model_args, structure_name = await self._get_model_args(
chat_log, structure_name, structure
)
coordinator = self.entry.runtime_data
client = coordinator.client

Expand All @@ -974,7 +971,7 @@ async def _async_handle_chat_log( # noqa: C901
)
]
)
messages.extend(new_messages)
cast(list[MessageParam], model_args["messages"]).extend(new_messages)
except anthropic.AuthenticationError as err:
# Trigger coordinator to confirm the auth failure and trigger the reauth flow.
await coordinator.async_request_refresh()
Expand Down
17 changes: 8 additions & 9 deletions homeassistant/components/device_automation/condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.condition import (
Condition,
ConditionChecker,
ConditionCheckerType,
ConditionConfig,
)
Expand Down Expand Up @@ -54,6 +53,7 @@ class DeviceCondition(Condition):
"""Device condition."""

_config: ConfigType
_platform_checker: ConditionCheckerType

@classmethod
async def async_validate_complete_config(
Expand Down Expand Up @@ -87,20 +87,19 @@ def __init__(self, hass: HomeAssistant, config: ConditionConfig) -> None:
assert config.options is not None
self._config = config.options

async def async_get_checker(self) -> ConditionChecker:
"""Test a device condition."""
async def async_setup(self) -> None:
"""Set up a device condition."""
platform = await async_get_device_automation_platform(
self._hass, self._config[CONF_DOMAIN], DeviceAutomationType.CONDITION
)
platform_checker = platform.async_condition_from_config(
self._platform_checker = platform.async_condition_from_config(
self._hass, self._config
)

def checker(variables: TemplateVarsType = None, **kwargs: Any) -> bool:
result = platform_checker(self._hass, variables)
return result is not False

return checker
def _async_check(self, variables: TemplateVarsType = None, **kwargs: Any) -> bool:
"""Check the condition."""
result = self._platform_checker(self._hass, variables)
return result is not False


CONDITIONS: dict[str, type[Condition]] = {
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/dnsip/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ async def async_step_user(
):
errors["base"] = "invalid_hostname"
else:
# Uses hostname as unique ID, which is no longer allowed
# pylint: disable-next=hass-unique-id-ip-based
await self.async_set_unique_id(hostname)
self._abort_if_unique_id_configured()

Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/freebox/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ async def async_step_user(
self._data = user_input

# Check if already configured
# Uses the host/IP value from CONF_HOST as unique ID, which is no longer allowed
# pylint: disable-next=hass-unique-id-ip-based
await self.async_set_unique_id(self._data[CONF_HOST])
self._abort_if_unique_id_configured()

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/fumis/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
"iot_class": "cloud_polling",
"loggers": ["fumis"],
"quality_scale": "bronze",
"requirements": ["fumis==0.2.1"]
"requirements": ["fumis==0.3.0"]
}
5 changes: 1 addition & 4 deletions homeassistant/components/fumis/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,7 @@ class FumisSensorEntityDescription(SensorEntityDescription):
device_class=SensorDeviceClass.DURATION,
native_unit_of_measurement=UnitOfTime.HOURS,
entity_category=EntityCategory.DIAGNOSTIC,
has_fn=lambda data: (
data.controller.time_to_service is not None
and data.controller.time_to_service >= 0
),
has_fn=lambda data: data.controller.time_to_service is not None,
value_fn=lambda data: data.controller.time_to_service,
),
FumisSensorEntityDescription(
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/indevolt/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"integration_type": "device",
"iot_class": "local_polling",
"quality_scale": "bronze",
"requirements": ["indevolt-api==1.2.3"]
"requirements": ["indevolt-api==1.3.1"]
}
4 changes: 2 additions & 2 deletions homeassistant/components/nfandroidtv/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""The NFAndroidTV integration."""

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.const import CONF_NAME, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv, discovery
from homeassistant.helpers.typing import ConfigType
Expand All @@ -28,7 +28,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass,
Platform.NOTIFY,
DOMAIN,
dict(entry.data),
{CONF_NAME: entry.title, **entry.data},
hass.data[DATA_HASS_CONFIG],
)
)
Expand Down
15 changes: 4 additions & 11 deletions homeassistant/components/nfandroidtv/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import voluptuous as vol

from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_HOST, CONF_NAME
from homeassistant.const import CONF_HOST

from .const import DEFAULT_NAME, DOMAIN

Expand All @@ -26,24 +26,17 @@ async def async_step_user(
errors = {}

if user_input is not None:
self._async_abort_entries_match(
{CONF_HOST: user_input[CONF_HOST], CONF_NAME: user_input[CONF_NAME]}
)
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
if not (error := await self._async_try_connect(user_input[CONF_HOST])):
return self.async_create_entry(
title=user_input[CONF_NAME],
title=f"{DEFAULT_NAME} ({user_input[CONF_HOST]})",
data=user_input,
)
errors["base"] = error

return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Required(CONF_NAME, default=DEFAULT_NAME): str,
}
),
data_schema=vol.Schema({vol.Required(CONF_HOST): str}),
errors=errors,
)

Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/nobo_hub/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
)
from .entity import NoboBaseEntity

PARALLEL_UPDATES = 0

SUPPORT_FLAGS = (
ClimateEntityFeature.PRESET_MODE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
)
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/nobo_hub/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
)
from .entity import NoboBaseEntity

PARALLEL_UPDATES = 0


async def async_setup_entry(
hass: HomeAssistant,
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/nobo_hub/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
from .const import ATTR_SERIAL, ATTR_ZONE_ID, DOMAIN, NOBO_MANUFACTURER
from .entity import NoboBaseEntity

PARALLEL_UPDATES = 0


async def async_setup_entry(
hass: HomeAssistant,
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/panasonic_viera/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,7 @@ async def async_load_data(self, config: dict[str, Any]) -> None:
self._data[CONF_PORT] = self._data.get(CONF_PORT, DEFAULT_PORT)
self._data[CONF_ON_ACTION] = self._data.get(CONF_ON_ACTION)

# Uses the host/IP value from CONF_HOST as unique ID, which is no longer allowed
# pylint: disable-next=hass-unique-id-ip-based
await self.async_set_unique_id(self._data[CONF_HOST])
self._abort_if_unique_id_configured()
5 changes: 4 additions & 1 deletion homeassistant/components/rainmachine/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ async def async_step_homekit_zeroconf(
# A new rain machine: We will change out the unique id
# for the mac address once we authenticate, however we want to
# prevent multiple different rain machines on the same network
# from being shown in discovery
# from being shown in discovery.
# Uses the discovered IP address as a temporary unique ID for
# discovery de-duplication until the MAC address is available.
# pylint: disable-next=hass-unique-id-ip-based
await self.async_set_unique_id(ip_address)
self._abort_if_unique_id_configured()
self.discovered_ip_address = ip_address
Expand Down
Loading
Loading