Full refactoring plan covering 48 issues from 4 parallel reviews (code, security, architecture, UI/UX). 6 phases + final review + tests.
33 KiB
ha-text-ai v2.4.0 — Полный план исправлений
Дата: 2026-03-12 Версия: 2.3.0 → 2.4.0 Принцип: Без breaking changes. Один релиз. Статус: В работе
Оглавление
- Принципы обратной совместимости
- Workflow разработки
- Фаза 0: Подготовка инфраструктуры
- Фаза 1: Security Hardening
- Фаза 2: Critical Bug Fixes
- Фаза 3: Concurrency & State
- Фаза 4: Dead Code & Deduplication
- Фаза 5: Coordinator Refactoring
- Фаза 6: UI/UX & Translations
- Финал: Full Review + Tests + CI/CD
- Трекинг прогресса
Принципы обратной совместимости
| Что | Правило |
|---|---|
| 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-ключей)
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:
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+ местах:
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:
_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:
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— при вводе endpointconfig_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
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 назначается после.
Станет:
- Убрать все
async_create_taskиз__init__ - Переместить
self._history_fileназначение ПЕРЕД задачами - Создать метод
async_initialize():
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()
- В
__init__.py:async_setup_entryвызыватьawait coordinator.async_initialize()после конструктора.
2.2. Удалить деструктивный цикл async_set [CRITICAL]
Файл: coordinator.py:778-781
Удалить полностью цикл:
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
Заменить:
temp_temperature = temperature or self.temperature
temp_max_tokens = max_tokens or self.max_tokens
На:
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
# Было: 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, не результат).
Исправление: Заменить:
await self.hass.async_add_executor_job(self._rotate_history_files)
На:
await self._rotate_history_files()
Или сделать _rotate_history_files синхронной (предпочтительнее — она делает файловые операции).
2.6. Исправить _is_rate_limited — сбрасывать после успеха [HIGH]
Файл: coordinator.py
В async_process_message после успешного API-вызова:
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 для совместимости:
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__
self._request_lock = asyncio.Lock()
В async_process_question:
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) на:
# 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
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
# Было: 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
# Было: 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— уже есть в HAasync-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:
# Было: 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:
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_historylist_request_lock(для записи в историю)
Класс:
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()_metricsdict_total_tokens,_prompt_tokens,_completion_tokens_successful_requests,_failed_requests_average_latency,_max_latency,_min_latency
Класс:
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 строк:
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 читает данные из координатора. Обновить доступ:
# Было: 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:
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:
- Code Review
- Security Review
- Architectural Review
- 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 (новый)
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. Подготовка | [ ] | |||
| 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)