mirror of
https://github.com/smkrv/ha-text-ai.git
synced 2026-07-22 15:14:01 +08:00
Release v2.0.0
This commit is contained in:
@@ -4,6 +4,8 @@ from __future__ import annotations
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import asyncio
|
||||
from homeassistant.helpers.import_platform import async_import_platform
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Dict, Optional
|
||||
@@ -235,7 +237,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
hass=hass,
|
||||
client=None, # Будет установлен позже
|
||||
model=model,
|
||||
update_interval=int(entry.data.get(CONF_REQUEST_INTERVAL, DEFAULT_REQUEST_INTERVAL)),
|
||||
update_interval=timedelta(seconds=int(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),
|
||||
@@ -248,7 +250,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"config_entry": entry,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
for platform in PLATFORMS:
|
||||
hass.async_create_task(
|
||||
async_import_platform(hass, f"{DOMAIN}.{platform}", DOMAIN)
|
||||
)
|
||||
await asyncio.sleep(0)
|
||||
return True
|
||||
|
||||
except Exception as ex:
|
||||
|
||||
@@ -31,7 +31,7 @@ CONF_REQUEST_INTERVAL: Final = "request_interval"
|
||||
CONF_INSTANCE: Final = "instance"
|
||||
|
||||
# Default values
|
||||
DEFAULT_MODEL: Final = "gpt-3.5-turbo" # Обновлено на актуальную модель
|
||||
DEFAULT_MODEL: Final = "gpt-4o"
|
||||
DEFAULT_TEMPERATURE: Final = 0.7
|
||||
DEFAULT_MAX_TOKENS: Final = 1000
|
||||
DEFAULT_REQUEST_INTERVAL: Final = 1.0
|
||||
@@ -64,6 +64,32 @@ ATTR_TEMPERATURE: Final = "temperature"
|
||||
ATTR_MAX_TOKENS: Final = "max_tokens"
|
||||
ATTR_SYSTEM_PROMPT: Final = "system_prompt"
|
||||
|
||||
# Sensor attributes
|
||||
ATTR_TOTAL_RESPONSES: Final = "total_responses"
|
||||
ATTR_TOTAL_ERRORS: Final = "total_errors"
|
||||
ATTR_AVG_RESPONSE_TIME: Final = "average_response_time"
|
||||
ATTR_LAST_REQUEST_TIME: Final = "last_request_time"
|
||||
ATTR_LAST_ERROR: Final = "last_error"
|
||||
ATTR_IS_PROCESSING: Final = "is_processing"
|
||||
ATTR_IS_RATE_LIMITED: Final = "is_rate_limited"
|
||||
ATTR_IS_MAINTENANCE: Final = "is_maintenance"
|
||||
ATTR_API_VERSION: Final = "api_version"
|
||||
ATTR_ENDPOINT_STATUS: Final = "endpoint_status"
|
||||
ATTR_PERFORMANCE_METRICS: Final = "performance_metrics"
|
||||
ATTR_HISTORY_SIZE: Final = "history_size"
|
||||
ATTR_UPTIME: Final = "uptime"
|
||||
ATTR_API_PROVIDER: Final = "api_provider"
|
||||
|
||||
# Sensor metrics
|
||||
METRIC_TOTAL_TOKENS: Final = "total_tokens"
|
||||
METRIC_PROMPT_TOKENS: Final = "prompt_tokens"
|
||||
METRIC_COMPLETION_TOKENS: Final = "completion_tokens"
|
||||
METRIC_SUCCESSFUL_REQUESTS: Final = "successful_requests"
|
||||
METRIC_FAILED_REQUESTS: Final = "failed_requests"
|
||||
METRIC_AVERAGE_LATENCY: Final = "average_latency"
|
||||
METRIC_MAX_LATENCY: Final = "max_latency"
|
||||
METRIC_MIN_LATENCY: Final = "min_latency"
|
||||
|
||||
# Error messages
|
||||
ERROR_INVALID_API_KEY: Final = "invalid_api_key"
|
||||
ERROR_CANNOT_CONNECT: Final = "cannot_connect"
|
||||
@@ -76,6 +102,25 @@ ERROR_TIMEOUT: Final = "timeout_error"
|
||||
ERROR_INVALID_INSTANCE: Final = "invalid_instance"
|
||||
ERROR_NAME_EXISTS: Final = "name_exists"
|
||||
|
||||
# Entity attributes
|
||||
ENTITY_ICON: Final = "mdi:robot"
|
||||
ENTITY_ICON_ERROR: Final = "mdi:robot-dead"
|
||||
ENTITY_ICON_PROCESSING: Final = "mdi:robot-excited"
|
||||
|
||||
# State attributes
|
||||
STATE_READY: Final = "ready"
|
||||
STATE_PROCESSING: Final = "processing"
|
||||
STATE_ERROR: Final = "error"
|
||||
STATE_INITIALIZING: Final = "initializing"
|
||||
STATE_MAINTENANCE: Final = "maintenance"
|
||||
STATE_RATE_LIMITED: Final = "rate_limited"
|
||||
STATE_DISCONNECTED: Final = "disconnected"
|
||||
|
||||
# Event names
|
||||
EVENT_RESPONSE_RECEIVED: Final = f"{DOMAIN}_response_received"
|
||||
EVENT_ERROR_OCCURRED: Final = f"{DOMAIN}_error_occurred"
|
||||
EVENT_STATE_CHANGED: Final = f"{DOMAIN}_state_changed"
|
||||
|
||||
# Service schema constants
|
||||
SERVICE_SCHEMA_ASK_QUESTION = vol.Schema({
|
||||
vol.Required(CONF_INSTANCE): cv.string,
|
||||
@@ -128,18 +173,3 @@ CONFIG_SCHEMA = vol.Schema({
|
||||
)
|
||||
})
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
# Entity attributes
|
||||
ENTITY_ICON: Final = "mdi:robot"
|
||||
ENTITY_ICON_ERROR: Final = "mdi:robot-dead"
|
||||
ENTITY_ICON_PROCESSING: Final = "mdi:robot-excited"
|
||||
|
||||
# State attributes
|
||||
STATE_READY: Final = "ready"
|
||||
STATE_PROCESSING: Final = "processing"
|
||||
STATE_ERROR: Final = "error"
|
||||
|
||||
# Event names
|
||||
EVENT_RESPONSE_RECEIVED: Final = f"{DOMAIN}_response_received"
|
||||
EVENT_ERROR_OCCURRED: Final = f"{DOMAIN}_error_occurred"
|
||||
EVENT_STATE_CHANGED: Final = f"{DOMAIN}_state_changed"
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
"cannot_connect": "Failed to connect to API service",
|
||||
"invalid_model": "Selected model is not available",
|
||||
"rate_limit": "Rate limit exceeded",
|
||||
"context_length": "Context length exceeded",
|
||||
"rate_limit_exceeded": "API rate limit exceeded",
|
||||
"maintenance": "Service is under maintenance",
|
||||
"invalid_response": "Invalid API response received",
|
||||
"api_error": "API service error occurred",
|
||||
"timeout": "Request timed out",
|
||||
"invalid_instance": "Invalid instance specified",
|
||||
@@ -53,85 +57,105 @@
|
||||
"services": {
|
||||
"ask_question": {
|
||||
"name": "Ask Question",
|
||||
"description": "Send a question to the specified AI instance",
|
||||
"description": "Send a question to the AI model and receive a detailed response. The response will be stored in the conversation history and can be retrieved later.",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "The HA Text AI instance to use"
|
||||
"description": "Name of the HA Text AI instance to use",
|
||||
"required": true
|
||||
},
|
||||
"question": {
|
||||
"name": "Question",
|
||||
"description": "Your question or prompt"
|
||||
"description": "Your question or prompt for the AI assistant",
|
||||
"required": true
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "System Prompt",
|
||||
"description": "Optional context for this question"
|
||||
"description": "Optional system prompt to set context for this specific question",
|
||||
"required": false
|
||||
},
|
||||
"model": {
|
||||
"name": "Model",
|
||||
"description": "Override default model for this request"
|
||||
"description": "Select AI model to use (optional, overrides default setting)",
|
||||
"required": false
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Temperature",
|
||||
"description": "Override default creativity setting"
|
||||
"description": "Controls response creativity (0.0-2.0)",
|
||||
"required": false,
|
||||
"default": 0.7
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Max Tokens",
|
||||
"description": "Override default response length limit"
|
||||
"description": "Maximum length of the response (1-4096 tokens)",
|
||||
"required": false,
|
||||
"default": 1000
|
||||
}
|
||||
}
|
||||
},
|
||||
"clear_history": {
|
||||
"name": "Clear History",
|
||||
"description": "Delete conversation history for specified instance",
|
||||
"description": "Delete all stored questions and responses from the conversation history",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "The HA Text AI instance to clear history for"
|
||||
"description": "Name of the HA Text AI instance to clear history for",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_history": {
|
||||
"name": "Get History",
|
||||
"description": "Retrieve conversation history for specified instance",
|
||||
"description": "Retrieve conversation history with optional filtering and sorting",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "The HA Text AI instance to get history from"
|
||||
"description": "Name of the HA Text AI instance to get history from",
|
||||
"required": true
|
||||
},
|
||||
"limit": {
|
||||
"name": "Limit",
|
||||
"description": "Maximum number of entries (1-100)"
|
||||
"description": "Number of conversations to return (1-100)",
|
||||
"required": false,
|
||||
"default": 10
|
||||
},
|
||||
"filter_model": {
|
||||
"name": "Filter Model",
|
||||
"description": "Filter by specific AI model"
|
||||
"description": "Filter conversations by specific AI model",
|
||||
"required": false
|
||||
},
|
||||
"start_date": {
|
||||
"name": "Start Date",
|
||||
"description": "Filter from specific date/time"
|
||||
"description": "Filter conversations starting from this date/time",
|
||||
"required": false
|
||||
},
|
||||
"include_metadata": {
|
||||
"name": "Include Metadata",
|
||||
"description": "Include additional response information"
|
||||
"description": "Include additional information like tokens used, response time, etc.",
|
||||
"required": false,
|
||||
"default": false
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "Sort Order",
|
||||
"description": "Sort order (newest/oldest first)"
|
||||
"description": "Sort order for results (newest or oldest first)",
|
||||
"required": false,
|
||||
"default": "desc"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_system_prompt": {
|
||||
"name": "Set System Prompt",
|
||||
"description": "Set default behavior for specified instance",
|
||||
"description": "Set default system behavior instructions for all future conversations",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Instance",
|
||||
"description": "The HA Text AI instance to configure"
|
||||
"description": "Name of the HA Text AI instance to set system prompt for",
|
||||
"required": true
|
||||
},
|
||||
"prompt": {
|
||||
"name": "System Prompt",
|
||||
"description": "Default behavior instructions"
|
||||
"description": "Instructions that define how the AI should behave and respond",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -146,6 +170,8 @@
|
||||
"error": "Error",
|
||||
"disconnected": "Disconnected",
|
||||
"rate_limited": "Rate Limited",
|
||||
"maintenance": "Maintenance",
|
||||
"initializing": "Initializing",
|
||||
"retrying": "Retrying",
|
||||
"queued": "Queued"
|
||||
},
|
||||
@@ -185,6 +211,60 @@
|
||||
},
|
||||
"tokens_used": {
|
||||
"name": "Total Tokens Used"
|
||||
},
|
||||
"average_response_time": {
|
||||
"name": "Average Response Time"
|
||||
},
|
||||
"last_request_time": {
|
||||
"name": "Last Request Time"
|
||||
},
|
||||
"is_processing": {
|
||||
"name": "Processing Status"
|
||||
},
|
||||
"is_rate_limited": {
|
||||
"name": "Rate Limited Status"
|
||||
},
|
||||
"is_maintenance": {
|
||||
"name": "Maintenance Status"
|
||||
},
|
||||
"api_version": {
|
||||
"name": "API Version"
|
||||
},
|
||||
"endpoint_status": {
|
||||
"name": "Endpoint Status"
|
||||
},
|
||||
"performance_metrics": {
|
||||
"name": "Performance Metrics"
|
||||
},
|
||||
"history_size": {
|
||||
"name": "History Size"
|
||||
},
|
||||
"uptime": {
|
||||
"name": "Uptime"
|
||||
},
|
||||
"total_tokens": {
|
||||
"name": "Total Tokens"
|
||||
},
|
||||
"prompt_tokens": {
|
||||
"name": "Prompt Tokens"
|
||||
},
|
||||
"completion_tokens": {
|
||||
"name": "Completion Tokens"
|
||||
},
|
||||
"successful_requests": {
|
||||
"name": "Successful Requests"
|
||||
},
|
||||
"failed_requests": {
|
||||
"name": "Failed Requests"
|
||||
},
|
||||
"average_latency": {
|
||||
"name": "Average Latency"
|
||||
},
|
||||
"max_latency": {
|
||||
"name": "Maximum Latency"
|
||||
},
|
||||
"min_latency": {
|
||||
"name": "Minimum Latency"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"provider": {
|
||||
"title": "Выбор AI провайдера",
|
||||
"description": "Выберите провайдера AI сервиса для этого экземпляра",
|
||||
"data": {
|
||||
"api_provider": "API провайдер"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"title": "Настройка экземпляра HA Text AI",
|
||||
"description": "Настройте новый экземпляр AI ассистента с выбранным провайдером",
|
||||
"data": {
|
||||
"name": "Название экземпляра (например, 'GPT Ассистент', 'Claude Помощник')",
|
||||
"api_key": "API ключ для аутентификации",
|
||||
"model": "Используемая AI модель",
|
||||
"temperature": "Креативность ответов (0-2, чем ниже, тем точнее)",
|
||||
"max_tokens": "Максимальная длина ответа (1-4096 токенов)",
|
||||
"api_endpoint": "Пользовательский URL API (необязательно)",
|
||||
"request_interval": "Минимальный интервал между запросами (0.1-60 секунд)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"name_exists": "Экземпляр с таким именем уже существует",
|
||||
"invalid_name": "Недопустимое имя экземпляра",
|
||||
"invalid_auth": "Ошибка аутентификации - проверьте API ключ",
|
||||
"invalid_api_key": "Недействительный API ключ - проверьте учетные данные",
|
||||
"cannot_connect": "Не удалось подключиться к API сервису",
|
||||
"invalid_model": "Выбранная модель недоступна",
|
||||
"rate_limit": "Превышен лимит запросов",
|
||||
"context_length": "Превышена длина контекста",
|
||||
"rate_limit_exceeded": "Превышен лимит API запросов",
|
||||
"maintenance": "Сервис находится на техническом обслуживании",
|
||||
"invalid_response": "Получен некорректный ответ API",
|
||||
"api_error": "Произошла ошибка API сервиса",
|
||||
"timeout": "Превышено время ожидания запроса",
|
||||
"invalid_instance": "Указан недействительный экземпляр",
|
||||
"unknown": "Произошла непредвиденная ошибка"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Обновление настроек экземпляра",
|
||||
"description": "Измените настройки для этого экземпляра AI ассистента",
|
||||
"data": {
|
||||
"model": "AI модель",
|
||||
"temperature": "Креативность ответов (0-2)",
|
||||
"max_tokens": "Максимальная длина ответа (1-4096)",
|
||||
"request_interval": "Минимальный интервал запросов (0.1-60 секунд)"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"ask_question": {
|
||||
"name": "Задать вопрос",
|
||||
"description": "Отправьте вопрос AI модели и получите подробный ответ. Ответ будет сохранен в истории разговора и может быть получен позже.",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Экземпляр",
|
||||
"description": "Название экземпляра HA Text AI для использования",
|
||||
"required": true
|
||||
},
|
||||
"question": {
|
||||
"name": "Вопрос",
|
||||
"description": "Ваш вопрос или запрос для AI ассистента",
|
||||
"required": true
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "Системный промпт",
|
||||
"description": "Необязательный системный промпт для установки контекста этого вопроса",
|
||||
"required": false
|
||||
},
|
||||
"model": {
|
||||
"name": "Модель",
|
||||
"description": "Выберите AI модель (необязательно, переопределяет настройки по умолчанию)",
|
||||
"required": false
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Temperature",
|
||||
"description": "Управляет креативностью ответов (0.0-2.0)",
|
||||
"required": false,
|
||||
"default": 0.7
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Max Tokens",
|
||||
"description": "Максимальная длина ответа (1-4096 токенов)",
|
||||
"required": false,
|
||||
"default": 1000
|
||||
}
|
||||
}
|
||||
},
|
||||
"clear_history": {
|
||||
"name": "Очистить историю",
|
||||
"description": "Удалить все сохраненные вопросы и ответы из истории разговора",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Экземпляр",
|
||||
"description": "Название экземпляра HA Text AI для очистки истории",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_history": {
|
||||
"name": "Получить историю",
|
||||
"description": "Получить историю разговора с дополнительной фильтрацией и сортировкой",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Экземпляр",
|
||||
"description": "Название экземпляра HA Text AI для получения истории",
|
||||
"required": true
|
||||
},
|
||||
"limit": {
|
||||
"name": "Лимит",
|
||||
"description": "Количество разговоров для возврата (1-100)",
|
||||
"required": false,
|
||||
"default": 10
|
||||
},
|
||||
"filter_model": {
|
||||
"name": "Фильтр модели",
|
||||
"description": "Фильтровать разговоры по конкретной AI модели",
|
||||
"required": false
|
||||
},
|
||||
"start_date": {
|
||||
"name": "Начальная дата",
|
||||
"description": "Фильтровать разговоры начиная с этой даты/времени",
|
||||
"required": false
|
||||
},
|
||||
"include_metadata": {
|
||||
"name": "Включить метаданные",
|
||||
"description": "Включить дополнительную информацию, такую как использованные токены, время ответа и т.д.",
|
||||
"required": false,
|
||||
"default": false
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "Порядок сортировки",
|
||||
"description": "Порядок сортировки результатов (сначала новые или старые)",
|
||||
"required": false,
|
||||
"default": "desc"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_system_prompt": {
|
||||
"name": "Установить системный промпт",
|
||||
"description": "Установить инструкции поведения по умолчанию для всех будущих разговоров",
|
||||
"fields": {
|
||||
"instance": {
|
||||
"name": "Экземпляр",
|
||||
"description": "Название экземпляра HA Text AI для настройки",
|
||||
"required": true
|
||||
},
|
||||
"prompt": {
|
||||
"name": "Системный промпт",
|
||||
"description": "Инструкции, определяющие поведение и ответы AI",
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"ha_text_ai": {
|
||||
"name": "{name}",
|
||||
"state": {
|
||||
"ready": "Готов",
|
||||
"processing": "Обработка",
|
||||
"error": "Ошибка",
|
||||
"disconnected": "Отключен",
|
||||
"rate_limited": "Превышен лимит",
|
||||
"maintenance": "Обслуживание",
|
||||
"initializing": "Инициализация",
|
||||
"retrying": "Повторная попытка",
|
||||
"queued": "В очереди"
|
||||
},
|
||||
"state_attributes": {
|
||||
"question": {
|
||||
"name": "Последний вопрос"
|
||||
},
|
||||
"response": {
|
||||
"name": "Последний ответ"
|
||||
},
|
||||
"model": {
|
||||
"name": "Текущая модель"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "Temperature"
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "Max Tokens"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "Системный промпт"
|
||||
},
|
||||
"response_time": {
|
||||
"name": "Время последнего ответа"
|
||||
},
|
||||
"total_responses": {
|
||||
"name": "Всего ответов"
|
||||
},
|
||||
"error_count": {
|
||||
"name": "Количество ошибок"
|
||||
},
|
||||
"last_error": {
|
||||
"name": "Последняя ошибка"
|
||||
},
|
||||
"api_status": {
|
||||
"name": "Статус API"
|
||||
},
|
||||
"tokens_used": {
|
||||
"name": "Всего использовано токенов"
|
||||
},
|
||||
"average_response_time": {
|
||||
"name": "Среднее время ответа"
|
||||
},
|
||||
"last_request_time": {
|
||||
"name": "Время последнего запроса"
|
||||
},
|
||||
"is_processing": {
|
||||
"name": "Статус обработки"
|
||||
},
|
||||
"is_rate_limited": {
|
||||
"name": "Статус ограничения запросов"
|
||||
},
|
||||
"is_maintenance": {
|
||||
"name": "Статус обслуживания"
|
||||
},
|
||||
"api_version": {
|
||||
"name": "Версия API"
|
||||
},
|
||||
"endpoint_status": {
|
||||
"name": "Статус конечной точки"
|
||||
},
|
||||
"performance_metrics": {
|
||||
"name": "Метрики производительности"
|
||||
},
|
||||
"history_size": {
|
||||
"name": "Размер истории"
|
||||
},
|
||||
"uptime": {
|
||||
"name": "Время работы"
|
||||
},
|
||||
"total_tokens": {
|
||||
"name": "Всего токенов"
|
||||
},
|
||||
"prompt_tokens": {
|
||||
"name": "Токены промпта"
|
||||
},
|
||||
"completion_tokens": {
|
||||
"name": "Токены завершения"
|
||||
},
|
||||
"successful_requests": {
|
||||
"name": "Успешные запросы"
|
||||
},
|
||||
"failed_requests": {
|
||||
"name": "Неудачные запросы"
|
||||
},
|
||||
"average_latency": {
|
||||
"name": "Средняя задержка"
|
||||
},
|
||||
"max_latency": {
|
||||
"name": "Максимальная задержка"
|
||||
},
|
||||
"min_latency": {
|
||||
"name": "Минимальная задержка"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user