"""The Anthropic integration.""" from __future__ import annotations from functools import partial import anthropic from homeassistant.config_entries import ConfigEntry, ConfigSubentry from homeassistant.const import CONF_API_KEY, Platform from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import ( config_validation as cv, device_registry as dr, entity_registry as er, ) from homeassistant.helpers.typing import ConfigType from .const import ( CONF_CHAT_MODEL, DEFAULT_CONVERSATION_NAME, DOMAIN, LOGGER, RECOMMENDED_CHAT_MODEL, ) PLATFORMS = (Platform.CONVERSATION,) CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN) type AnthropicConfigEntry = ConfigEntry[anthropic.AsyncClient] async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up Anthropic.""" await async_migrate_integration(hass) return True async def async_setup_entry(hass: HomeAssistant, entry: AnthropicConfigEntry) -> bool: """Set up Anthropic from a config entry.""" client = await hass.async_add_executor_job( partial(anthropic.AsyncAnthropic, api_key=entry.data[CONF_API_KEY]) ) try: # Use model from first conversation subentry for validation subentries = list(entry.subentries.values()) if subentries: model_id = subentries[0].data.get(CONF_CHAT_MODEL, RECOMMENDED_CHAT_MODEL) else: model_id = RECOMMENDED_CHAT_MODEL model = await client.models.retrieve(model_id=model_id, timeout=10.0) LOGGER.debug("Anthropic model: %s", model.display_name) except anthropic.AuthenticationError as err: LOGGER.error("Invalid API key: %s", err) return False except anthropic.AnthropicError as err: raise ConfigEntryNotReady(err) from err entry.runtime_data = client await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) entry.async_on_unload(entry.add_update_listener(async_update_options)) return True async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Unload Anthropic.""" return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) async def async_update_options( hass: HomeAssistant, entry: AnthropicConfigEntry ) -> None: """Update options.""" await hass.config_entries.async_reload(entry.entry_id) async def async_migrate_integration(hass: HomeAssistant) -> None: """Migrate integration entry structure.""" entries = hass.config_entries.async_entries(DOMAIN) if not any(entry.version == 1 for entry in entries): return api_keys_entries: dict[str, ConfigEntry] = {} entity_registry = er.async_get(hass) device_registry = dr.async_get(hass) for entry in entries: use_existing = False subentry = ConfigSubentry( data=entry.options, subentry_type="conversation", title=entry.title, unique_id=None, ) if entry.data[CONF_API_KEY] not in api_keys_entries: use_existing = True api_keys_entries[entry.data[CONF_API_KEY]] = entry parent_entry = api_keys_entries[entry.data[CONF_API_KEY]] hass.config_entries.async_add_subentry(parent_entry, subentry) conversation_entity = entity_registry.async_get_entity_id( "conversation", DOMAIN, entry.entry_id, ) if conversation_entity is not None: entity_registry.async_update_entity( conversation_entity, config_entry_id=parent_entry.entry_id, config_subentry_id=subentry.subentry_id, new_unique_id=subentry.subentry_id, ) device = device_registry.async_get_device( identifiers={(DOMAIN, entry.entry_id)} ) if device is not None: device_registry.async_update_device( device.id, new_identifiers={(DOMAIN, subentry.subentry_id)}, add_config_subentry_id=subentry.subentry_id, add_config_entry_id=parent_entry.entry_id, ) if parent_entry.entry_id != entry.entry_id: device_registry.async_update_device( device.id, remove_config_entry_id=entry.entry_id, ) else: device_registry.async_update_device( device.id, remove_config_entry_id=entry.entry_id, remove_config_subentry_id=None, ) if not use_existing: await hass.config_entries.async_remove(entry.entry_id) else: hass.config_entries.async_update_entry( entry, title=DEFAULT_CONVERSATION_NAME, options={}, version=2, minor_version=2, ) async def async_migrate_entry(hass: HomeAssistant, entry: AnthropicConfigEntry) -> bool: """Migrate entry.""" LOGGER.debug("Migrating from version %s:%s", entry.version, entry.minor_version) if entry.version > 2: # This means the user has downgraded from a future version return False if entry.version == 2 and entry.minor_version == 1: # Correct broken device migration in Home Assistant Core 2025.7.0b0-2025.7.0b1 device_registry = dr.async_get(hass) for device in dr.async_entries_for_config_entry( device_registry, entry.entry_id ): device_registry.async_update_device( device.id, remove_config_entry_id=entry.entry_id, remove_config_subentry_id=None, ) hass.config_entries.async_update_entry(entry, minor_version=2) LOGGER.debug( "Migration to version %s:%s successful", entry.version, entry.minor_version ) return True