From 9779e5552d969cff1aaad37cc971ec6064b25bbc Mon Sep 17 00:00:00 2001 From: SMKRV Date: Sun, 24 Nov 2024 20:12:03 +0300 Subject: [PATCH] Release v2.0.0 --- custom_components/ha_text_ai/__init__.py | 49 ++++++++++-------------- custom_components/ha_text_ai/sensor.py | 39 +++++++++++++------ 2 files changed, 48 insertions(+), 40 deletions(-) diff --git a/custom_components/ha_text_ai/__init__.py b/custom_components/ha_text_ai/__init__.py index 1e109af..4067724 100644 --- a/custom_components/ha_text_ai/__init__.py +++ b/custom_components/ha_text_ai/__init__.py @@ -66,6 +66,19 @@ SERVICE_SCHEMA_GET_HISTORY = vol.Schema({ vol.Optional("filter_model"): cv.string, }) +def get_coordinator_by_instance(hass: HomeAssistant, instance: str) -> HATextAICoordinator: + """Get coordinator by instance name.""" + # Убираем префикс "sensor." если он есть + if instance.startswith("sensor."): + instance = instance.replace("sensor.ha_text_ai_", "", 1) + + # Ищем координатор по instance_name + for entry_id, coord in hass.data[DOMAIN].items(): + if isinstance(coord, HATextAICoordinator) and coord.instance_name.lower() == instance.lower(): + return coord + + raise HomeAssistantError(f"Instance {instance} not found") + async def async_setup(hass: HomeAssistant, config: Dict[str, Any]) -> bool: """Set up the HA Text AI component.""" hass.data.setdefault(DOMAIN, {}) @@ -82,12 +95,8 @@ async def async_setup(hass: HomeAssistant, config: Dict[str, Any]) -> bool: async def async_ask_question(call: ServiceCall) -> None: """Handle ask_question service.""" - instance = call.data["instance"] - coordinator = hass.data[DOMAIN].get(instance) - if not coordinator: - raise HomeAssistantError(f"Instance {instance} not found") - try: + coordinator = get_coordinator_by_instance(hass, call.data["instance"]) await coordinator.async_ask_question( question=call.data["question"], model=call.data.get("model"), @@ -101,29 +110,20 @@ async def async_setup(hass: HomeAssistant, config: Dict[str, Any]) -> bool: async def async_clear_history(call: ServiceCall) -> None: """Handle clear_history service.""" - instance = call.data["instance"] - coordinator = hass.data[DOMAIN].get(instance) - if not coordinator: - raise HomeAssistantError(f"Instance {instance} not found") - try: + coordinator = get_coordinator_by_instance(hass, call.data["instance"]) await coordinator.async_clear_history() except Exception as err: _LOGGER.error("Error clearing history: %s", str(err)) raise HomeAssistantError(f"Failed to clear history: {str(err)}") - async def async_get_history(call: ServiceCall) -> None: + async def async_get_history(call: ServiceCall) -> list: """Handle get_history service.""" - instance = call.data["instance"] - coordinator = hass.data[DOMAIN].get(instance) - if not coordinator: - raise HomeAssistantError(f"Instance {instance} not found") - try: + coordinator = get_coordinator_by_instance(hass, call.data["instance"]) return await coordinator.async_get_history( limit=call.data.get("limit"), - filter_model=call.data.get("filter_model"), - instance=instance + filter_model=call.data.get("filter_model") ) except Exception as err: _LOGGER.error("Error getting history: %s", str(err)) @@ -131,12 +131,8 @@ async def async_setup(hass: HomeAssistant, config: Dict[str, Any]) -> bool: async def async_set_system_prompt(call: ServiceCall) -> None: """Handle set_system_prompt service.""" - instance = call.data["instance"] - coordinator = hass.data[DOMAIN].get(instance) - if not coordinator: - raise HomeAssistantError(f"Instance {instance} not found") - try: + coordinator = get_coordinator_by_instance(hass, call.data["instance"]) await coordinator.async_set_system_prompt(call.data["prompt"]) except Exception as err: _LOGGER.error("Error setting system prompt: %s", str(err)) @@ -238,16 +234,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: "model": model, } - # Получаем интервал обновления - update_interval = timedelta( - seconds=entry.data.get(CONF_REQUEST_INTERVAL, DEFAULT_REQUEST_INTERVAL) - ) - coordinator = HATextAICoordinator( hass=hass, client=api_client, model=model, - update_interval=entry.data.get(CONF_REQUEST_INTERVAL, DEFAULT_REQUEST_INTERVAL), # Передаем числовое значение + update_interval=entry.data.get(CONF_REQUEST_INTERVAL, DEFAULT_REQUEST_INTERVAL), instance_name=instance_name, max_tokens=entry.data.get(CONF_MAX_TOKENS, DEFAULT_MAX_TOKENS), temperature=entry.data.get(CONF_TEMPERATURE, DEFAULT_TEMPERATURE), diff --git a/custom_components/ha_text_ai/sensor.py b/custom_components/ha_text_ai/sensor.py index 35637e2..4a6c3cf 100644 --- a/custom_components/ha_text_ai/sensor.py +++ b/custom_components/ha_text_ai/sensor.py @@ -38,6 +38,7 @@ from .const import ( ATTR_API_STATUS, ATTR_RESPONSE, ATTR_QUESTION, + ATTR_CONVERSATION_HISTORY, METRIC_TOTAL_TOKENS, METRIC_PROMPT_TOKENS, METRIC_COMPLETION_TOKENS, @@ -94,28 +95,32 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): self._config_entry = config_entry self._instance_name = coordinator.instance_name + self._conversation_history = [] + self._system_prompt = None - # Добавляем явное указание платформы и домена + # Entity attributes self._attr_has_entity_name = True self.entity_id = f"sensor.ha_text_ai_{self._instance_name}" self._attr_name = f"HA Text AI {self._instance_name}" self._attr_unique_id = f"{config_entry.entry_id}" - # Явно указываем принадлежность к интеграции + # Entity description self.entity_description = SensorEntityDescription( key=f"ha_text_ai_{self._instance_name}", name=self._attr_name, entity_registry_enabled_default=True, ) - # Добавляем информацию об интеграции + # Integration info self._attr_platform = "sensor" self._attr_domain = DOMAIN + # State tracking self._current_state = STATE_INITIALIZING self._error_count = 0 self._last_error = None self._last_update = None + self._is_processing = False # Device info model = config_entry.data.get(CONF_MODEL, "Unknown") @@ -160,6 +165,9 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): ATTR_TOTAL_ERRORS: self._error_count, ATTR_LAST_ERROR: self._last_error, "instance_name": self._instance_name, + ATTR_SYSTEM_PROMPT: self._system_prompt, + ATTR_CONVERSATION_HISTORY: self._conversation_history, + ATTR_IS_PROCESSING: self._is_processing, } if not self.coordinator.data: @@ -167,12 +175,11 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): data = self.coordinator.data - # Основные атрибуты + # Basic attributes for attr in [ ATTR_TOTAL_RESPONSES, ATTR_AVG_RESPONSE_TIME, ATTR_LAST_REQUEST_TIME, - ATTR_IS_PROCESSING, ATTR_IS_RATE_LIMITED, ATTR_IS_MAINTENANCE, ATTR_API_VERSION, @@ -180,13 +187,12 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): ATTR_PERFORMANCE_METRICS, ATTR_HISTORY_SIZE, ATTR_UPTIME, - ATTR_SYSTEM_PROMPT, ]: value = data.get(attr) if value is not None: attributes[attr] = value - # Метрики + # Metrics metrics = data.get("metrics", {}) if isinstance(metrics, dict): for metric in [ @@ -203,7 +209,7 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): if value is not None: attributes[metric] = value - # Последний ответ + # Last response last_response = data.get("last_response", {}) if isinstance(last_response, dict): attributes[ATTR_RESPONSE] = last_response.get("response", "") @@ -226,8 +232,9 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): try: data = self.coordinator.data - # Обновляем состояние - if data.get("is_processing"): + # Update state + self._is_processing = data.get("is_processing", False) + if self._is_processing: self._current_state = STATE_PROCESSING elif data.get("is_rate_limited"): self._current_state = STATE_RATE_LIMITED @@ -240,7 +247,17 @@ class HATextAISensor(CoordinatorEntity, SensorEntity): else: self._current_state = STATE_READY - # Обновляем метрики + # Update history + history = data.get("conversation_history", []) + if isinstance(history, list): + self._conversation_history = history + + # Update system prompt + system_prompt = data.get("system_prompt") + if system_prompt is not None: + self._system_prompt = system_prompt + + # Update metrics metrics = data.get("metrics", {}) if isinstance(metrics, dict): self._error_count = metrics.get("total_errors", self._error_count)