docs: Update README with current model names, remove outdated YAML config section

- Update recommended models to current versions (Claude 4.6, GPT-5, Gemini 3.1, DeepSeek-V3)
- Add Google Gemini to features list
- Remove YAML configuration section (integration is config_entry_only)
- Remove deleted Api status attribute from documentation
- Update conversation_history display count (1 → 5)
- Fix FAQ with current model names and token limits
- Remove internal spec file
This commit is contained in:
SMKRV
2026-03-12 12:05:43 +03:00
parent 618ad34ccc
commit ad36352fe8
2 changed files with 19 additions and 931 deletions
+17 -78
View File
@@ -12,7 +12,7 @@
</div>
<p align="center">
Transform your smart home experience with powerful AI assistance powered by multiple AI providers including OpenAI GPT, DeepSeek and Anthropic Claude models. Get intelligent responses, automate complex scenarios, and enhance your home automation with advanced natural language processing.
Transform your smart home experience with powerful AI assistance powered by multiple AI providers including OpenAI GPT, Anthropic Claude, DeepSeek and Google Gemini models. Get intelligent responses, automate complex scenarios, and enhance your home automation with advanced natural language processing.
</p>
@@ -28,7 +28,7 @@ Transform your smart home experience with powerful AI assistance powered by mult
## 🌟 Features
- 🧠 **Multi-Provider AI Integration**: Support for OpenAI GPT, DeepSeek and Anthropic Claude models
- 🧠 **Multi-Provider AI Integration**: Support for OpenAI GPT, Anthropic Claude, DeepSeek and Google Gemini models
- 💬 **Advanced Language Processing**: Context-aware, multi-turn conversations
- 📝 **Enhanced Memory Management**: Secure file-based history storage
-**Performance Optimization**: Efficient token usage and smart rate limiting
@@ -44,6 +44,7 @@ Transform your smart home experience with powerful AI assistance powered by mult
- Support for OpenAI GPT models
- Anthropic Claude integration
- DeepSeek integration
- Google Gemini integration
- Custom API endpoints
- Flexible model selection
@@ -136,17 +137,17 @@ Transform your smart home experience with powerful AI assistance powered by mult
- **GPT-5 mini** - A cost-effective and fast model, suitable for most tasks
#### Anthropic Claude Models
- **Claude Opus 4.1** - The most capable model for handling complex tasks
- **Claude Sonnet 4** - Offers a balance between performance and cost
- **Claude Haiku 4** - The fastest and most economical option in the series
- **Claude Opus 4.6** - The most capable model for handling complex tasks
- **Claude Sonnet 4.6** - Offers a balance between performance and cost
- **Claude Haiku 4.5** - The fastest and most economical option in the series
#### DeepSeek Models
- **DeepSeek-V3.1** - A general-purpose model for a wide range of tasks
- **DeepSeek-V3** - A general-purpose model for a wide range of tasks
- **DeepSeek-R1** - A specialized model focused on reasoning and coding
#### Google Gemini Models
- **Gemini 2.5 Pro & 2.5 Flash** - The newest and most advanced models available
- **Gemini 2.0 Pro & 2.0 Flash** - Previous generation models that are still powerful and efficient
- **Gemini 3.1 Pro** - The newest and most advanced model available
- **Gemini 3.1 Flash Lite** - Fastest and most cost-efficient model for high-volume workloads
<details>
<summary>🌐 Potentially Compatible Providers</summary>
@@ -201,7 +202,7 @@ If the integration is not found in the default repository:
1. Download the latest release
2. Extract and copy `custom_components/ha_text_ai` to your `custom_components` directory
3. Restart Home Assistant
4. Add configuration via UI or YAML
4. Add configuration via UI (Settings → Devices & Services → Add Integration)
## ⚙️ Configuration
@@ -211,66 +212,7 @@ If the integration is not found in the default repository:
3. Search for "HA Text AI"
4. Follow the configuration steps
<details>
<summary>📦 Via YAML (Advanced)</summary>
### Platform Configuration (Global Settings)
```yaml
ha_text_ai:
api_provider: openai # Required
api_key: !secret ai_api_key # Required
model: gpt-4o # Strongly recommended
temperature: 0.7 # Optional
max_tokens: 1000 # Optional
request_interval: 1.0 # Optional
api_endpoint: https://api.openai.com/v1 # Required
system_prompt: | # Optional
You are a home automation expert assistant.
Focus on practical and efficient solutions.
```
### Sensor Configuration
```yaml
sensor:
- platform: ha_text_ai
name: "My AI Assistant" # Required, unique identifier
api_provider: openai # Optional (inherits from platform)
model: "gpt-4o" # Optional
temperature: 0.7 # Optional
max_tokens: 1000 # Optional
```
### 📋 Configuration Parameters
#### Platform Configuration
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `api_provider` | String | ✅ | - | AI service provider (openai, anthropic, deepseek, gemini) |
| `api_key` | String | ✅ | - | Authentication key for AI service |
| `model` | String | ⚠️ | gpt-4o-mini | Strongly recommended: Specific AI model to use. Default varies by provider |
| `temperature` | Float | ❌ | 0.1 | Response creativity level (0.0-2.0) |
| `max_tokens` | Integer | ❌ | 1000 | Maximum response length |
| `request_interval` | Float | ❌ | 1.0 | Delay between API requests |
| `api_endpoint` | URL | ⚠️ | Provider default | Custom API endpoint |
| `system_prompt` | String | ❌ | - | Default context for AI interactions |
| `max_history_size` | Integer | ❌ | 50 | Maximum number of conversation entries to store |
| `context_messages` | Integer | ❌ | 5 | Number of previous messages to include in context (1-20) |
#### Sensor Configuration
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `platform` | String | ✅ | - | Must be `ha_text_ai` |
| `name` | String | ✅ | - | Unique sensor identifier |
| `api_provider` | String | ❌ | Platform setting | Override global provider |
| `model` | String | ⚠️ | Provider default | Recommended: Override global model (gpt-4o-mini, deepseek-chat, gemini-2.0-flash) |
| `temperature` | Float | ❌ | 0.1 | Override global temperature |
| `max_tokens` | Integer | ❌ | 1000 | Override global max tokens |
</details>
> **Note:** This integration is configured exclusively through the UI (config entries). YAML configuration is not supported.
## 🛠️ Available Services
@@ -289,7 +231,7 @@ sensor:
service: ha_text_ai.ask_question
data:
question: "What's the optimal temperature for sleeping?"
model: "claude-3.5-sonnet" # optional
model: "claude-sonnet-4-6-20260217" # optional
temperature: 0.5 # optional
max_tokens: 500 # optional
context_messages: 10 #optional, number of previous messages to include in context, default: 5
@@ -305,7 +247,7 @@ response_text: "The optimal sleeping temperature is 65-68°F (18-20°C)..."
tokens_used: 150
prompt_tokens: 50
completion_tokens: 100
model_used: "claude-3.5-sonnet"
model_used: "claude-sonnet-4-6-20260217"
instance: "sensor.ha_text_ai_gpt"
question: "What's the optimal temperature for sleeping?"
timestamp: "2025-02-09T16:57:00.000Z"
@@ -563,9 +505,6 @@ automation:
#### System Status
```yaml
# Current operational readiness of the AI service API
{{ state_attr('sensor.ha_text_ai_gpt', 'Api status') }} # ready
# Indicates if a request is currently being processed
{{ state_attr('sensor.ha_text_ai_gpt', 'Is processing') }} # false
@@ -608,7 +547,7 @@ automation:
# Number of entries in current history file
{{ state_attr('sensor.ha_text_ai_gpt', 'History size') }} # 0
# Last few conversation entries (limited to 1 for performance)
# Last few conversation entries (last 5 for performance)
{{ state_attr('sensor.ha_text_ai_gpt', 'conversation_history') }} # [...]
```
@@ -656,7 +595,7 @@ Conversation history stored in `.storage/ha_text_ai_history/` directory:
A: OpenAI (GPT models), Anthropic (Claude models), DeepSeek, Google Gemini, and OpenRouter are officially supported, with many other OpenAI-compatible providers working as well.
**Q: How can I reduce API costs?**
A: Use gpt-4o-mini or claude-3.5-haiku for most queries, implement caching, and optimize token usage.
A: Use gpt-5-mini or claude-haiku-4-5 for most queries, implement caching, and optimize token usage.
**Q: Are there limitations on the number of requests?**
A: Depends on your API provider's plan. We recommend monitoring usage and implementing request throttling via `request_interval` configuration.
@@ -668,7 +607,7 @@ A: Yes, you can configure custom endpoints and use any compatible model by speci
A: Simply change the model parameter in your configuration or service calls to use the desired provider's model.
**Q: What are the token limits for different models?**
A: Token limits vary by provider and model. OpenAI's gpt-4o supports up to 128K tokens, Claude 3.5 Sonnet supports up to 200K tokens, while smaller models typically have 8K-32K limits. Check your provider's documentation for specific limits.
A: Token limits vary by provider and model. OpenAI's GPT-5 supports up to 1M context tokens, Claude Opus 4.6 supports up to 1M tokens, Gemini 3.1 Pro supports up to 1M tokens, while smaller models typically have 128K-200K limits. Check your provider's documentation for specific limits.
**Q: How do I monitor token usage?**
A: Use the sensor attributes like `Total tokens`, `Prompt tokens`, and `Completion tokens` to track usage. You can also create automations to alert you when usage exceeds certain thresholds.
@@ -686,7 +625,7 @@ A: History is stored in files under the `.storage/ha_text_ai_history/` directory
A: Yes, archived history files are stored with timestamps and can be accessed manually if needed.
**Q: How much history is kept?**
A: By default, up to 100 conversations are stored, but this can be configured. Files are automatically rotated when they reach 1MB.
A: By default, up to 50 conversations are stored (max 200), configurable via UI. Files are automatically rotated when they reach 1MB.
## 🤝 Contributing
-851
View File
@@ -1,851 +0,0 @@
# ha-text-ai v2.4.0 — Полный план исправлений
**Дата**: 2026-03-12
**Версия**: 2.3.0 → 2.4.0
**Принцип**: Без breaking changes. Один релиз.
**Статус**: В работе
---
## Оглавление
- [Принципы обратной совместимости](#принципы-обратной-совместимости)
- [Workflow разработки](#workflow-разработки)
- [Фаза 0: Подготовка инфраструктуры](#фаза-0-подготовка-инфраструктуры)
- [Фаза 1: Security Hardening](#фаза-1-security-hardening)
- [Фаза 2: Critical Bug Fixes](#фаза-2-critical-bug-fixes)
- [Фаза 3: Concurrency & State](#фаза-3-concurrency--state)
- [Фаза 4: Dead Code & Deduplication](#фаза-4-dead-code--deduplication)
- [Фаза 5: Coordinator Refactoring](#фаза-5-coordinator-refactoring)
- [Фаза 6: UI/UX & Translations](#фаза-6-uiux--translations)
- [Финал: Full Review + Tests + CI/CD](#финал-full-review--tests--cicd)
- [Трекинг прогресса](#трекинг-прогресса)
---
## Принципы обратной совместимости
| Что | Правило |
|-----|---------|
| Sensor attributes | Все существующие сохраняются. Новые добавляются. Избыточные остаются для совместимости |
| Service response format | Структура ответа `ask_question`, `get_history` не меняется |
| Service schema | Поля не удаляются, не переименовываются. Новые — только `vol.Optional` |
| Entity ID | Формат `sensor.ha_text_ai_{name}` не меняется |
| Config entry data | Добавление новых ключей OK. Существующие не удаляются. Миграция прозрачная |
| History file format | JSON-структура сохраняется. Новые поля — опциональные |
| `hass.data[DOMAIN]` | Ключи по `entry_id` — не менять |
---
## Workflow разработки
```
Для каждой фазы:
1. Реализация фикса
2. 4 параллельных review-агента (Code, Security, Architecture, UI/UX)
3. Документация
4. Исправление замечаний review
5. Повторная проверка (4 агента)
6. Документация
7. Локальный коммит
После ВСЕХ фаз:
1. Полный финальный review (все 4 агента)
2. Добавление тестов + CI/CD workflow
3. Финальный коммит
4. Push
```
---
## Фаза 0: Подготовка инфраструктуры
**Цель**: Создать фундамент для остальных фаз без изменения поведения.
### 0.1. Создать `utils.py`
**Файл**: `custom_components/ha_text_ai/utils.py` (новый)
Извлечь из `config_flow.py`:
- Функцию `normalize_name()` (config_flow.py:351-370)
- Вспомогательную функцию `get_file_hash()` из `__init__.py:108-114`
- Хелпер для безопасного логирования (фильтрация API-ключей)
```python
def safe_log_data(data: dict, sensitive_keys: tuple = (CONF_API_KEY,)) -> dict:
"""Filter sensitive keys from data for safe logging."""
return {k: "***" if k in sensitive_keys else v for k, v in data.items()}
```
**Обратная совместимость**: `config_flow.py` сохраняет `normalize_name` как re-export:
```python
from .utils import normalize_name # backward compat
```
Это позволит `coordinator.py:29` (`from .config_flow import normalize_name`) продолжать работать, пока мы не обновим все импорты.
### 0.2. Создать `providers.py`
**Файл**: `custom_components/ha_text_ai/providers.py` (новый)
Единый реестр провайдеров вместо дублирования dispatch-таблиц в 4+ местах:
```python
PROVIDER_REGISTRY = {
API_PROVIDER_OPENAI: {
"default_model": DEFAULT_MODEL_OPENAI,
"default_endpoint": DEFAULT_ENDPOINT_OPENAI,
"auth_header": "Authorization",
"auth_prefix": "Bearer ",
"check_path": "/models",
},
API_PROVIDER_ANTHROPIC: { ... },
API_PROVIDER_DEEPSEEK: { ... },
API_PROVIDER_GEMINI: { ... },
}
def get_provider_config(provider: str) -> dict: ...
def get_default_endpoint(provider: str) -> str: ...
def get_default_model(provider: str) -> str: ...
def build_auth_headers(provider: str, api_key: str) -> dict: ...
```
### 0.3. Обновить импорты
Во всех файлах заменить:
- `from .config_flow import normalize_name``from .utils import normalize_name`
- Inline dispatch-таблицы → вызовы `providers.py`
**Коммит**: `refactor: Extract utils.py and providers.py for code deduplication`
---
## Фаза 1: Security Hardening
**Цель**: Устранить все уязвимости безопасности.
### 1.1. Убрать логирование API-ключей [CRITICAL]
**Файлы и строки**:
- `__init__.py:312``_LOGGER.debug(f"Setting up HA Text AI entry: {entry.data}")`
- `config_flow.py:160``_LOGGER.debug(f"Provider step input data: {user_input}")`
- `config_flow.py:465``_LOGGER.debug(f"Creating config entry with data: {entry_data}")`
- `sensor.py:116``_LOGGER.debug(f"Initializing sensor with config entry: {config_entry.data}")`
**Исправление**: Заменить на `safe_log_data()` из `utils.py`:
```python
_LOGGER.debug("Setting up HA Text AI entry: %s", safe_log_data(entry.data))
```
Заодно перевести все `_LOGGER` вызовы с f-string на `%s`-форматирование (lazy evaluation).
**Масштаб**: Все файлы, все `_LOGGER.debug(f"...")``_LOGGER.debug("...", arg)`.
### 1.2. Валидация endpoint — HTTPS-only + защита от SSRF [HIGH]
**Файлы**: `utils.py` (новая функция), `config_flow.py`, `__init__.py`
Добавить в `utils.py`:
```python
from urllib.parse import urlparse
import ipaddress
def validate_endpoint(url: str) -> tuple[bool, str]:
"""Validate API endpoint URL. Returns (is_valid, error_message)."""
parsed = urlparse(url)
if parsed.scheme not in ("https",):
return False, "only_https_supported"
hostname = parsed.hostname
if not hostname:
return False, "invalid_url"
try:
ip = ipaddress.ip_address(hostname)
if ip.is_private or ip.is_loopback or ip.is_link_local:
return False, "private_ip_not_allowed"
except ValueError:
pass # hostname is a domain name, not IP — OK
return True, ""
```
Применить в:
- `config_flow.py:async_step_provider` — при вводе endpoint
- `config_flow.py:OptionsFlowHandler` — при смене endpoint
- `__init__.py:async_setup_entry` — перед созданием API client
**Обратная совместимость**: Существующие записи с HTTP endpoints продолжат работать (валидация только при создании/изменении). Добавить warning в лог при загрузке существующего HTTP endpoint.
### 1.3. Не показывать API-ключ в формах [HIGH]
**Файлы**: `config_flow.py:648-650, 224, 286, 329`
**Исправление**:
- В `_get_settings_schema` и во всех error re-display формах: НЕ передавать `default=data.get(CONF_API_KEY)`.
- Вместо `vol.Required(CONF_API_KEY, default=...)` использовать `vol.Optional(CONF_API_KEY, description={"suggested_value": ""})`.
- Если поле пустое — сохранять существующий ключ.
### 1.4. Требовать повторный ввод ключа при смене endpoint [HIGH]
**Файл**: `config_flow.py:OptionsFlowHandler.async_step_settings`
При обнаружении изменения endpoint:
- Очистить поле API key
- Показать предупреждение в описании поля
- Не отправлять старый ключ на новый endpoint при валидации
### 1.5. Убрать history_path из атрибутов сенсора [MEDIUM]
**Файл**: `coordinator.py:_get_limited_history`
Удалить `"history_path": self._history_file` из возвращаемого dict. Это внутренний путь FS.
### 1.6. Санитизировать ошибки в service response [MEDIUM]
**Файл**: `__init__.py:161`
Заменить `"error": str(err)` на `"error": "Request failed"`. Полную ошибку логировать через `_LOGGER.error`.
### 1.7. Добавить лимит длины для question и json_schema [LOW]
**Файл**: `__init__.py:73,80`
```python
vol.Required("question"): vol.All(cv.string, vol.Length(max=100000)),
vol.Optional("json_schema"): vol.All(cv.string, vol.Length(max=50000)),
```
### 1.8. Валидация Gemini API key [LOW]
**Файл**: `config_flow.py:263-278`, `__init__.py:279-285`
Для Gemini: выполнять реальный API-вызов `models.list()` вместо проверки "непустая строка".
**Коммит**: `fix: Security hardening — credential protection, SSRF prevention, input validation`
---
## Фаза 2: Critical Bug Fixes
**Цель**: Исправить баги, приводящие к потере данных и некорректному поведению.
### 2.1. Исправить инициализацию координатора [CRITICAL]
**Файл**: `coordinator.py:177-186`
**Было**: 5 fire-and-forget `async_create_task` в `__init__` + `_history_file` назначается после.
**Станет**:
1. Убрать все `async_create_task` из `__init__`
2. Переместить `self._history_file` назначение ПЕРЕД задачами
3. Создать метод `async_initialize()`:
```python
async def async_initialize(self) -> None:
"""Initialize coordinator: directories, history, metrics. Must be awaited."""
await self._create_history_dir()
await self._check_history_directory()
await self._initialize_metrics()
await self.async_initialize_history_file()
await self._migrate_history_from_txt_to_json()
```
4. В `__init__.py:async_setup_entry` вызывать `await coordinator.async_initialize()` после конструктора.
### 2.2. Удалить деструктивный цикл `async_set` [CRITICAL]
**Файл**: `coordinator.py:778-781`
Удалить полностью цикл:
```python
for entity_id in self.hass.states.async_entity_ids():
if entity_id.startswith(entity_id_base):
self.hass.states.async_set(entity_id, self._get_current_state())
```
`async_request_refresh()` на строке 775 уже обновляет состояние через координатор → `_handle_coordinator_update``async_write_ha_state()`.
### 2.3. Исправить `temperature=0` [HIGH]
**Файл**: `coordinator.py:870-873`
Заменить:
```python
temp_temperature = temperature or self.temperature
temp_max_tokens = max_tokens or self.max_tokens
```
На:
```python
temp_temperature = temperature if temperature is not None else self.temperature
temp_max_tokens = max_tokens if max_tokens is not None else self.max_tokens
temp_context = context_messages if context_messages is not None else self.context_messages
temp_system = system_prompt if system_prompt is not None else self._system_prompt
```
**Файл**: `__init__.py:76`
Заменить `cv.positive_float` на `vol.All(vol.Coerce(float), vol.Range(min=0.0, max=2.0))` для temperature.
### 2.4. Исправить retry loop — не ретраить нетранзиентные ошибки [HIGH]
**Файл**: `api_client.py:86-122`
```python
# Было: except Exception ловит HomeAssistantError (4xx ответы) и ретраит
# Станет:
for attempt in range(self.retry_count):
try:
async with self.session.post(...) as response:
if response.ok:
return await response.json()
error_data = {}
try:
error_data = await response.json()
except Exception:
error_data = {"raw": await response.text()}
if response.status == 429: # rate limit — retry
await asyncio.sleep(attempt + 1)
continue
if response.status >= 400: # client/server error — don't retry
raise HomeAssistantError(f"API error: {response.status}")
except (asyncio.TimeoutError, aiohttp.ClientError) as err:
if attempt == self.retry_count - 1:
raise HomeAssistantError(f"Connection failed: {err}") from err
await asyncio.sleep(attempt + 1)
```
### 2.5. Починить ротацию истории [HIGH]
**Файл**: `coordinator.py:588-592`
`_rotate_history_files``async def`, но вызывается через `async_add_executor_job` (запускает sync в потоке → получает coroutine, не результат).
**Исправление**: Заменить:
```python
await self.hass.async_add_executor_job(self._rotate_history_files)
```
На:
```python
await self._rotate_history_files()
```
Или сделать `_rotate_history_files` синхронной (предпочтительнее — она делает файловые операции).
### 2.6. Исправить `_is_rate_limited` — сбрасывать после успеха [HIGH]
**Файл**: `coordinator.py`
В `async_process_message` после успешного API-вызова:
```python
self._is_rate_limited = False
self.endpoint_status = "ready"
self._error_count = 0
```
### 2.7. `ConnectionError` не должен ставить `is_rate_limited` [HIGH]
**Файл**: `coordinator.py:361-364`
Убрать `"is_rate_limited": True` из маппинга `ConnectionError`.
### 2.8. Исправить `async_ask_question` — propagation ошибок [MEDIUM]
**Файл**: `coordinator.py:815-849`
`async_ask_question` — бессмысленная обёртка над `async_process_question`. Убрать лишний уровень индирекции, или добавить полезную логику (валидацию входных параметров).
**Файл**: `__init__.py:148-162`
Сервис `ask_question` глотает все ошибки. Добавить `raise HomeAssistantError` для критических ошибок, сохраняя `success: False` в response data для совместимости:
```python
except Exception as err:
_LOGGER.error("ask_question failed: %s", err)
response = {"success": False, "error": "Request failed", ...}
# НЕ raise — для обратной совместимости. Ошибка в response data.
return response
```
**Коммит**: `fix: Critical bugs — init race condition, data loss, temperature=0, retry logic, history rotation`
---
## Фаза 3: Concurrency & State
**Цель**: Устранить race conditions и проблемы управления состоянием.
### 3.1. Добавить `asyncio.Lock` для конкурентных запросов [MEDIUM]
**Файл**: `coordinator.py:__init__`
```python
self._request_lock = asyncio.Lock()
```
В `async_process_question`:
```python
async with self._request_lock:
# ... existing logic
```
### 3.2. Исправить Semaphore — убрать или сделать instance-level [MEDIUM]
**Файл**: `coordinator.py:668`
Убрать `async with asyncio.Semaphore(1):``DataUpdateCoordinator` уже сериализует `_async_update_data`.
### 3.3. Исправить `async_shutdown` — правильный ключ [HIGH]
**Файл**: `coordinator.py:1159`
Заменить `self.hass.data[DOMAIN].pop(self.instance_name, None)` на:
```python
# Cleanup handled by async_unload_entry in __init__.py
# Remove this line entirely — it uses wrong key (instance_name vs entry_id)
```
### 3.4. Исправить double timeout в `_make_request` [MEDIUM]
**Файл**: `api_client.py:98-103`
Убрать `async with timeout(self.api_timeout):``aiohttp ClientTimeout` достаточно. Убрать `from async_timeout import timeout`.
### 3.5. Кэшировать Gemini client [MEDIUM]
**Файл**: `api_client.py`
```python
def __init__(self, ...):
...
self._gemini_client = None
def _get_gemini_client(self, api_key):
if self._gemini_client is None:
self._gemini_client = genai.Client(api_key=api_key, ...)
return self._gemini_client
```
### 3.6. Исправить `get_history` — не мутировать оригинальные записи [MEDIUM]
**Файл**: `coordinator.py:1118-1125`
```python
# Было: entry["metadata"] = {...} # мутирует оригинал
# Станет:
import copy
enriched = copy.deepcopy(entry)
enriched["metadata"] = {...}
```
### 3.7. Персистировать system_prompt [LOW]
**Файл**: `coordinator.py`
Сохранять `_system_prompt` в файл `.storage/ha_text_ai_system_prompt_{name}.txt`. Загружать при инициализации. При reload/restart — восстанавливать.
### 3.8. Исправить `os.rename` в async контексте [MEDIUM]
**Файл**: `coordinator.py:1047`
```python
# Было: os.rename(old_history_file, backup_file)
# Станет:
await self.hass.async_add_executor_job(os.rename, old_history_file, backup_file)
```
**Коммит**: `fix: Concurrency — request locking, state management, Gemini client caching, system prompt persistence`
---
## Фаза 4: Dead Code & Deduplication
**Цель**: Удалить мёртвый код и устранить дублирование.
### 4.1. Удалить мёртвый код
| Файл | Что удалить | Строки |
|------|-------------|--------|
| `coordinator.py` | `_check_memory_available()` + `import psutil` | 1138-1154, 17 |
| `coordinator.py` | `_sync_write_history_entry()` | 535-586 |
| `coordinator.py` | `AsyncFileHandler` class (заменить на прямой `aiofiles.open`) | 51-63 |
| `api_client.py` | `check_connection()` (всегда broken) | 326-... |
| `api_client.py` | `self._closed` flag (никто не проверяет) | в shutdown() |
| `api_client.py` | `import HomeAssistant` (не используется) | 16 |
| `api_client.py` | `import datetime, timedelta` (не используются) | 14 |
| `coordinator.py` | `import re` (не используется) | 18 |
| `__init__.py` | `import TypeVar` + `ConfigType` (не нужен) | 16, 69 |
| `__init__.py` | `import timedelta` (не используется) | 15 |
### 4.2. Консолидировать schema определения
**Файл**: `const.py:188-256`
Удалить мёртвые schema:
- `SERVICE_SCHEMA_ASK_QUESTION` (неиспользуемый, конфликтует с `__init__.py`)
- `SERVICE_SCHEMA_SET_SYSTEM_PROMPT` (дублирует `__init__.py`)
- `SERVICE_SCHEMA_GET_HISTORY` (дублирует `__init__.py`)
- `CONFIG_SCHEMA` (не используется)
Оставить единственное определение в `__init__.py`.
### 4.3. Удалить неиспользуемые зависимости из `manifest.json`
**Файл**: `manifest.json`
Удалить:
- `anthropic>=0.8.0` — не импортируется (API через `aiohttp`)
- `openai>=1.12.0` — не импортируется (API через `aiohttp`)
- `certifi>=2024.2.2` — уже есть в HA
- `async-timeout>=4.0.0` — deprecated, заменяем на `asyncio.timeout`
Обновить:
- `aiohttp>=3.9.4` (security fix: CVE-2024-23334, CVE-2024-23829)
### 4.4. Использовать `providers.py` вместо inline dispatch
Заменить во всех файлах inline-таблицы `default_endpoint`/`default_model`/headers:
**`__init__.py:326-362`**:
```python
# Было: 4 if/elif для endpoint + 4 if/elif для model + 4 if/elif для headers
# Станет:
from .providers import get_provider_config, build_auth_headers
config = get_provider_config(provider)
endpoint = entry_data.get(CONF_API_ENDPOINT) or config["default_endpoint"]
headers = build_auth_headers(provider, api_key)
```
**`config_flow.py:104-117, 487-502`**: аналогично.
### 4.5. Консолидировать `_async_validate_api`
**Файлы**: `config_flow.py` — два идентичных метода в `HATextAIConfigFlow` (372-407) и `OptionsFlowHandler` (517-549).
Извлечь в `utils.py`:
```python
async def validate_api_connection(hass, provider, endpoint, api_key) -> tuple[bool, str]:
```
### 4.6. Унифицировать timeout
Заменить все `from async_timeout import timeout` на `from asyncio import timeout` (Python 3.12+, HA 2024.12.0 требует).
**Файлы**: `api_client.py:3`, `__init__.py:19`
**Коммит**: `refactor: Remove dead code, consolidate schemas, unify provider dispatch, clean dependencies`
---
## Фаза 5: Coordinator Refactoring
**Цель**: Разбить god object `coordinator.py` (~1160 строк) на модули с чёткими ответственностями.
### 5.1. Создать `history.py`
**Файл**: `custom_components/ha_text_ai/history.py` (новый)
Извлечь из `coordinator.py`:
- `_create_history_dir()`
- `_check_history_directory()`
- `async_initialize_history_file()`
- `_migrate_history_from_txt_to_json()`
- `_write_history_entry()`
- `_rotate_history()` / `_rotate_history_files()`
- `async_clear_history()`
- `async_get_history()`
- `_update_history()`
- `_get_limited_history()`
- `_conversation_history` list
- `_request_lock` (для записи в историю)
Класс:
```python
class HistoryManager:
def __init__(self, hass, instance_name, normalized_name, history_dir, max_history_size):
...
async def async_initialize(self): ...
async def write_entry(self, entry): ...
async def get_history(self, limit, filter_model, ...): ...
async def clear(self): ...
def get_limited(self, max_entries): ...
@property
def conversation_history(self) -> list: ...
@property
def size(self) -> int: ...
```
### 5.2. Создать `metrics.py`
**Файл**: `custom_components/ha_text_ai/metrics.py` (новый)
Извлечь из `coordinator.py`:
- `_initialize_metrics()`
- `_update_metrics()`
- `_save_metrics()` / `_load_metrics()`
- `_metrics` dict
- `_total_tokens`, `_prompt_tokens`, `_completion_tokens`
- `_successful_requests`, `_failed_requests`
- `_average_latency`, `_max_latency`, `_min_latency`
Класс:
```python
class MetricsTracker:
def __init__(self, hass, normalized_name, storage_dir):
...
async def async_initialize(self): ...
def update(self, tokens_used, prompt_tokens, completion_tokens, latency): ...
async def save(self): ...
@property
def summary(self) -> dict: ...
```
### 5.3. Упростить `coordinator.py`
После извлечения `coordinator.py` сократится с ~1160 до ~400-500 строк:
```python
class HATextAICoordinator(DataUpdateCoordinator):
def __init__(self, hass, client, config, ...):
self.history = HistoryManager(...)
self.metrics = MetricsTracker(...)
...
async def async_initialize(self):
await self.history.async_initialize()
await self.metrics.async_initialize()
async def async_process_question(self, question, **kwargs):
async with self._request_lock:
...
response = await self.client.create(...)
self.metrics.update(...)
await self.history.write_entry(...)
return response
async def _async_update_data(self):
...
```
### 5.4. Обновить `sensor.py`
`sensor.py` читает данные из координатора. Обновить доступ:
```python
# Было: self.coordinator._conversation_history
# Станет: self.coordinator.history.conversation_history
```
### 5.5. Добавить лимит архивов истории
**Файл**: `history.py` (в рамках ротации)
При ротации — хранить максимум 5 архивных файлов. Удалять самые старые.
**Коммит**: `refactor: Split coordinator into coordinator, history, and metrics modules`
---
## Фаза 6: UI/UX & Translations
**Цель**: Исправить проблемы пользовательского интерфейса и переводов.
### 6.1. Исправить дублированный JSON-ключ `"provider"` [CRITICAL]
**Файлы**: Все 8 файлов в `translations/`
В каждом файле под `config.step` есть два ключа `"provider"`. Объединить в один с правильным содержимым (title + data из второго, т.к. первый перезаписывается).
### 6.2. Добавить `strings.json` [LOW]
**Файл**: `custom_components/ha_text_ai/strings.json` (новый)
Скопировать `translations/en.json``strings.json` (HA convention: `strings.json` = authoritative English source).
### 6.3. Извлечь form schema builder для config flow [HIGH]
**Файл**: `config_flow.py`
Создать `_build_provider_schema(user_input, provider, errors)` по образцу `_get_settings_schema` из `OptionsFlowHandler`. Использовать единый метод во всех 5 местах re-display формы. Все поля всегда присутствуют.
### 6.4. Использовать `NumberSelector` для числовых полей [MEDIUM]
**Файл**: `config_flow.py`
Для temperature, max_tokens, request_interval, api_timeout, context_messages, max_history_size:
```python
vol.Required(CONF_TEMPERATURE, default=DEFAULT_TEMPERATURE): selector.NumberSelector(
selector.NumberSelectorConfig(min=0.0, max=2.0, step=0.1, mode="slider")
),
```
### 6.5. Выровнять переводы с кодом
**Файлы**: Все `translations/*.json`
| Действие | Что |
|----------|-----|
| Удалить | Мёртвые error keys: `invalid_characters`, `name_too_long`, `history_storage_error`, `history_rotation_error`, `history_file_access_error`, `invalid_model`, `rate_limit`, `context_length` и др. |
| Удалить | Мёртвые state keys: `retrying`, `queued` |
| Добавить | Недостающие attribute keys для реальных атрибутов |
| Оставить | Все существующие используемые ключи |
### 6.6. Исправить ошибки в переводах
| Язык | Проблема | Исправление |
|------|----------|-------------|
| `it.json` | "Istanze" (мн.ч.) → "Istanza" (ед.ч.) | Заменить все вхождения |
| `hi.json` | "instance" = "उदाहरण" (example) | Заменить на "इंस्टैंस" |
| `sr.json` | "disconnected" = "Прекључено" (switched) | Заменить на "Искључено" |
| `de.json` | Опции селектора не переведены | Перевести: "OpenAI (kompatibel)" и т.д. |
### 6.7. Прочие UI-исправления
| # | Что | Файл |
|---|-----|------|
| A | `PLATFORMS: list[Platform] = [Platform.SENSOR]` | `const.py:20` |
| B | `supports_response=SupportsResponse.OPTIONAL` | `__init__.py:203` |
| C | `manufacturer="SMKRV"` вместо `"Community"` | `sensor.py:157` |
| D | `datetime` selector для `start_date` в services | `services.yaml:146-149` |
| E | Выровнять temperature default: 0.1 везде | `services.yaml:63` |
| F | Убрать "This service now returns..." из description | `services.yaml:6-7` |
| G | Убрать пустые массивы из manifest (`bluetooth`, `mqtt` и т.д.) | `manifest.json` |
| H | Убрать нестандартное копирование иконок в `www/` | `__init__.py:228-272` |
**Пункт H — детали**: Современные HA + HACS используют `icons/` directory напрямую. Удалить `_copy_icon_to_www()` и весь связанный код. Существующие файлы в `www/ha_text_ai/` не трогаем (пользователь может удалить сам).
### 6.8. Исправить 404 как успешную валидацию API
**Файлы**: `__init__.py:295`, `config_flow.py:399`
Только 200 = успех. 404 → `cannot_connect`.
**Коммит**: `fix: UI/UX — translations, config flow forms, sensor attributes, services.yaml alignment`
---
## Финал: Full Review + Tests + CI/CD
### F.1. Полный финальный review
Запуск всех 4 review-агентов на весь codebase:
1. Code Review
2. Security Review
3. Architectural Review
4. UI/UX Review
Исправить все найденные проблемы. Повторить до чистого прохода.
### F.2. Добавить тесты
**Файлы**: `tests/` (новая директория)
Минимальный набор тестов:
```
tests/
├── conftest.py # HA test fixtures
├── test_config_flow.py # Config flow + options flow
├── test_api_client.py # API client per provider
├── test_coordinator.py # Coordinator logic
├── test_sensor.py # Sensor entity
├── test_utils.py # Utility functions
├── test_history.py # History manager
├── test_metrics.py # Metrics tracker
└── test_providers.py # Provider registry
```
Ключевые тесты:
- Config flow: создание entry для каждого провайдера, validation errors, options flow
- API client: успешный ответ, retry на 429, НЕ retry на 400, timeout
- Coordinator: `temperature=0` работает, concurrent requests, history write/read
- Security: endpoint validation (HTTPS-only, no private IPs), API key не в логах
- History: write, read, rotate, archive cleanup
- Translations: все файлы имеют одинаковые ключи
### F.3. CI/CD workflow для тестов
**Файл**: `.github/workflows/tests.yaml` (новый)
```yaml
name: Tests
on:
push:
branches: [main, dev]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
pip install pytest pytest-asyncio pytest-homeassistant-custom-component
pip install -r requirements_test.txt
- name: Run tests
run: pytest tests/ -v
```
### F.4. Обновить version в manifest.json
`"version": "2.3.0"``"version": "2.4.0"`
### F.5. Финальные коммиты
```
test: Add comprehensive test suite for all components
chore: Add test workflow to CI/CD
chore: Bump version to 2.4.0
```
### F.6. Push
Только после успешного прохождения всех проверок.
---
## Трекинг прогресса
| Фаза | Статус | Дата начала | Дата завершения | Коммит |
|------|--------|-------------|-----------------|--------|
| 0. Подготовка | [x] | 2026-03-12 | 2026-03-12 | e7c8b22+ |
| 1. Security | [ ] | | | |
| 2. Critical Bugs | [ ] | | | |
| 3. Concurrency | [ ] | | | |
| 4. Dead Code | [ ] | | | |
| 5. Coordinator | [ ] | | | |
| 6. UI/UX | [ ] | | | |
| F. Final Review | [ ] | | | |
| F. Tests | [ ] | | | |
| F. CI/CD | [ ] | | | |
| F. Push | [ ] | | | |
---
## Новые файлы (итого)
```
custom_components/ha_text_ai/
├── utils.py (Phase 0)
├── providers.py (Phase 0)
├── history.py (Phase 5)
├── metrics.py (Phase 5)
├── strings.json (Phase 6)
tests/
├── conftest.py (Final)
├── test_*.py (Final)
.github/workflows/
├── tests.yaml (Final)
```
## Изменяемые файлы (все фазы)
```
custom_components/ha_text_ai/
├── __init__.py (Phases 1,2,4,6)
├── api_client.py (Phases 2,3,4)
├── config_flow.py (Phases 0,1,4,6)
├── const.py (Phases 4,6)
├── coordinator.py (Phases 1,2,3,4,5)
├── sensor.py (Phases 1,5,6)
├── manifest.json (Phases 4,6,Final)
├── services.yaml (Phase 6)
├── translations/*.json (Phase 6)
```