From d870cfbba6c83d7a283416f2b8fb2969b087f404 Mon Sep 17 00:00:00 2001 From: SMKRV Date: Sat, 23 Nov 2024 00:36:58 +0300 Subject: [PATCH] Release v2.0.0 --- custom_components/ha_text_ai/__init__.py | 1 + custom_components/ha_text_ai/config_flow.py | 54 +++++++++---------- custom_components/ha_text_ai/const.py | 2 + custom_components/ha_text_ai/coordinator.py | 10 +++- custom_components/ha_text_ai/sensor.py | 53 ++++++++++-------- .../ha_text_ai/translations/en.json | 13 ++++- 6 files changed, 80 insertions(+), 53 deletions(-) diff --git a/custom_components/ha_text_ai/__init__.py b/custom_components/ha_text_ai/__init__.py index d4833e6..c1ac090 100644 --- a/custom_components/ha_text_ai/__init__.py +++ b/custom_components/ha_text_ai/__init__.py @@ -252,6 +252,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: temperature=entry.data.get(CONF_TEMPERATURE, DEFAULT_TEMPERATURE), max_tokens=entry.data.get(CONF_MAX_TOKENS, DEFAULT_MAX_TOKENS), request_interval=float(entry.data.get(CONF_REQUEST_INTERVAL, DEFAULT_REQUEST_INTERVAL)), + name=entry.title, # Передаем имя из конфигурации session=session, is_anthropic=is_anthropic ) diff --git a/custom_components/ha_text_ai/config_flow.py b/custom_components/ha_text_ai/config_flow.py index b0c52f8..4913786 100644 --- a/custom_components/ha_text_ai/config_flow.py +++ b/custom_components/ha_text_ai/config_flow.py @@ -4,7 +4,7 @@ import voluptuous as vol import aiohttp from homeassistant import config_entries -from homeassistant.const import CONF_API_KEY, CONF_NAME +from homeassistant.const import CONF_API_KEY from homeassistant.core import callback from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession @@ -41,18 +41,13 @@ class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - def __init__(self): - """Initialize the config flow.""" - self._provider = None - async def async_step_user(self, user_input: Optional[Dict[str, Any]] = None) -> FlowResult: """Handle the initial step.""" if user_input is None: return self._show_provider_selection() if "provider" in user_input: - self._provider = user_input["provider"] - return self._show_provider_config(self._provider) + return self._show_provider_config(user_input["provider"]) return await self._process_configuration(user_input) @@ -70,10 +65,12 @@ class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Show configuration screen for selected provider.""" default_endpoint = DEFAULT_OPENAI_ENDPOINT if provider == API_PROVIDER_OPENAI else DEFAULT_ANTHROPIC_ENDPOINT + default_name = f"HA Text AI {len(self._async_current_entries()) + 1}" + return self.async_show_form( step_id="user", data_schema=vol.Schema({ - vol.Required(CONF_NAME): str, # Добавлено поле имени + vol.Required("name", default=default_name): str, vol.Required(CONF_API_PROVIDER): vol.In([provider]), vol.Required(CONF_API_KEY): str, vol.Required(CONF_MODEL, default=DEFAULT_MODEL): str, @@ -99,10 +96,8 @@ class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors = {} try: - # Создаем уникальный идентификатор на основе имени и эндпоинта - unique_id = f"{user_input[CONF_NAME]}_{user_input[CONF_API_ENDPOINT]}" - await self.async_set_unique_id(unique_id) - self._abort_if_unique_id_configured() + # Принудительная очистка существующих записей с тем же API-ключом + await self._async_cleanup_existing_entries(user_input[CONF_API_KEY]) session = async_get_clientsession(self.hass) @@ -112,7 +107,7 @@ class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): "Content-Type": "application/json" } - # Adjust headers based on provider + # Adjust headers and endpoint based on provider if user_input[CONF_API_PROVIDER] == API_PROVIDER_ANTHROPIC: headers = { "x-api-key": user_input[CONF_API_KEY], @@ -132,8 +127,12 @@ class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors["base"] = "cannot_connect" if not errors: + # Генерируем уникальный идентификатор на основе ключа + await self.async_set_unique_id(user_input[CONF_API_KEY]) + self._abort_if_unique_id_configured() + return self.async_create_entry( - title=user_input[CONF_NAME], + title=user_input.get("name", f"HA Text AI ({user_input[CONF_API_PROVIDER]})"), # Используем введенное имя data=user_input ) @@ -145,8 +144,9 @@ class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_show_form( step_id="user", data_schema=vol.Schema({ + vol.Required("name", default=user_input.get("name", f"HA Text AI {len(self._async_current_entries()) + 1}")): str, vol.Required(CONF_API_PROVIDER): vol.In([user_input[CONF_API_PROVIDER]]), - vol.Required(CONF_API_KEY, default=user_input.get(CONF_API_KEY, "")): str, + vol.Required(CONF_API_KEY): str, vol.Required(CONF_MODEL, default=user_input.get(CONF_MODEL, DEFAULT_MODEL)): str, vol.Optional(CONF_API_ENDPOINT, default=user_input.get(CONF_API_ENDPOINT, DEFAULT_OPENAI_ENDPOINT if user_input[CONF_API_PROVIDER] == API_PROVIDER_OPENAI @@ -168,19 +168,19 @@ class HATextAIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): errors=errors ) - async def _async_cleanup_existing_entries(self, api_key): - """Cleanup existing entries with the same API key.""" - existing_entries = [ - entry for entry in self.hass.config_entries.async_entries(DOMAIN) - if entry.data.get(CONF_API_KEY) == api_key - ] +async def _async_cleanup_existing_entries(self, api_key): + """Cleanup existing entries with the same API key.""" + existing_entries = [ + entry for entry in self.hass.config_entries.async_entries(DOMAIN) + if entry.data.get(CONF_API_KEY) == api_key + ] - for entry in existing_entries: - try: - await self.hass.config_entries.async_remove(entry.entry_id) - _LOGGER.info(f"Removed existing entry with ID: {entry.entry_id}") - except Exception as e: - _LOGGER.warning(f"Could not remove existing entry: {e}") + for entry in existing_entries: + try: + await self.hass.config_entries.async_remove(entry.entry_id) + _LOGGER.info(f"Removed existing entry with ID: {entry.entry_id}") + except Exception as e: + _LOGGER.warning(f"Could not remove existing entry: {e}") @staticmethod @callback diff --git a/custom_components/ha_text_ai/const.py b/custom_components/ha_text_ai/const.py index cf0a1bb..48c9e1b 100644 --- a/custom_components/ha_text_ai/const.py +++ b/custom_components/ha_text_ai/const.py @@ -7,6 +7,8 @@ from homeassistant.helpers import config_validation as cv # Domain and platforms DOMAIN: Final = "ha_text_ai" PLATFORMS: Final = [Platform.SENSOR] +CONF_NAME = "name" +DEFAULT_NAME = "HA Text AI" # New constants for providers CONF_API_PROVIDER: Final = "api_provider" diff --git a/custom_components/ha_text_ai/coordinator.py b/custom_components/ha_text_ai/coordinator.py index c2a1c00..9cd8c32 100644 --- a/custom_components/ha_text_ai/coordinator.py +++ b/custom_components/ha_text_ai/coordinator.py @@ -38,16 +38,17 @@ class HATextAICoordinator(DataUpdateCoordinator): temperature: float, max_tokens: int, request_interval: float, + name: str, session: Optional[Any] = None, is_anthropic: bool = False, ) -> None: super().__init__( hass, _LOGGER, - name=DOMAIN, + name=name, update_interval=timedelta(seconds=float(request_interval)), ) - + self._name = name self._validate_params(api_key, temperature, max_tokens) self._entity_id = None self.api_key = api_key @@ -512,6 +513,11 @@ class HATextAICoordinator(DataUpdateCoordinator): """Set entity ID.""" self._entity_id = value + @property + def name(self) -> str: + """Get coordinator name.""" + return self._name + @property def performance_metrics(self) -> Dict[str, Any]: """Return current performance metrics.""" diff --git a/custom_components/ha_text_ai/sensor.py b/custom_components/ha_text_ai/sensor.py index 7f77284..1ec5ad0 100644 --- a/custom_components/ha_text_ai/sensor.py +++ b/custom_components/ha_text_ai/sensor.py @@ -14,6 +14,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util import dt as dt_util +from homeassistant.util import slugify from .const import ( DOMAIN, @@ -62,31 +63,39 @@ async def async_setup_entry( class HATextAISensor(CoordinatorEntity, SensorEntity): """HA text AI Sensor.""" - def __init__( - self, - coordinator: HATextAICoordinator, - config_entry: ConfigEntry, - ) -> None: - """Initialize the sensor.""" - super().__init__(coordinator) - self._config_entry = config_entry +def __init__( + self, + coordinator: HATextAICoordinator, + config_entry: ConfigEntry, +) -> None: + """Initialize the sensor.""" + super().__init__(coordinator) + self._config_entry = config_entry - self._attr_unique_id = f"{config_entry.entry_id}_sensor" - self.entity_id = "sensor.ha_text_ai" - self._attr_name = "HA Text AI" + # Используем имя из конфигурации + self._name = config_entry.title - self._current_state = STATE_INITIALIZING - self._error_count = 0 - self._last_error = None + # Создаем уникальный ID используя имя + self._attr_unique_id = f"{config_entry.entry_id}_{slugify(self._name)}" - # Девайс инфо - self._attr_device_info = { - "identifiers": {(DOMAIN, self._attr_unique_id)}, - "name": "HA Text AI", - "manufacturer": "Community", - "model": f"{coordinator.model} ({self._config_entry.data.get(CONF_API_PROVIDER, 'Unknown')} provider)", - "sw_version": coordinator.api_version, - } + # Создаем entity_id используя имя + self.entity_id = f"sensor.ha_text_ai_{slugify(self._name)}" + + # Устанавливаем отображаемое имя + self._attr_name = self._name + + self._current_state = STATE_INITIALIZING + self._error_count = 0 + self._last_error = None + + # Обновляем device_info с использованием имени + self._attr_device_info = { + "identifiers": {(DOMAIN, self._attr_unique_id)}, + "name": self._name, # Используем имя из конфигурации + "manufacturer": "Community", + "model": f"{coordinator.model} ({self._config_entry.data.get(CONF_API_PROVIDER, 'Unknown')} provider)", + "sw_version": coordinator.api_version, + } @property def icon(self) -> str: diff --git a/custom_components/ha_text_ai/translations/en.json b/custom_components/ha_text_ai/translations/en.json index 3112300..43a1136 100644 --- a/custom_components/ha_text_ai/translations/en.json +++ b/custom_components/ha_text_ai/translations/en.json @@ -12,11 +12,12 @@ "max_tokens": "Maximum response length (1-4096 tokens)", "api_endpoint": "Custom API endpoint URL (optional)", "request_interval": "Minimum time between requests in seconds (min: 0.1)", - "name": "Integration name" + "name": "Name for this integration instance (e.g., 'GPT Assistant', 'Claude Helper')" } } }, "error": { + "invalid_name": "Invalid integration name", "invalid_auth": "Authentication failed - check your API key", "invalid_api_key": "Invalid API key - please verify your credentials", "cannot_connect": "Connection failed - check endpoint and network status", @@ -71,7 +72,15 @@ "entity": { "sensor": { "ha_text_ai": { - "name": "HA Text AI" + "name": "{name}", + "state": { + "initializing": "Initializing", + "ready": "Ready", + "processing": "Processing", + "error": "Error", + "disconnected": "Disconnected", + "rate_limited": "Rate Limited" + } } } }