diff --git a/README.md b/README.md index 8a96f68..02d5655 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,17 @@ ![pydantic version](https://img.shields.io/badge/pydantic-ha-yellowgreen?style=plastic&logo=fastapi) ![aiohttp version](https://img.shields.io/badge/aiohttp-ha-yellowgreen?style=plastic) ![Home Assistant](https://img.shields.io/badge/HomeAssistant-latest-yellowgreen?style=plastic&logo=homeassistant) + + [![Donate](https://img.shields.io/badge/donate-Tinkoff-FFDD2D.svg)](https://www.tinkoff.ru/rm/shutov.mikhail19/wUyu873109) ## Описание Компонент для управления устройствами [ZONT](https://zont-online.ru/) из Home Assistant. -Для входа в ваш аккаунт потребуется токен. Токен можно получить [здесь](https://lk.zont-online.ru/widget-api/v2). +Для входа в ваш аккаунт потребуется токен. Его можно получить из Home Assistant. +При добавлении устройства нажмите галочку "Получить токен". При каждом получении токена +создаётся новый. Что бы их не плодить на аккаунте ZONT, запишите полученный токен. +Как удалить токен описано [здесь](https://lk.zont-online.ru/widget-api/v2). После авторизации в Home Assistant (далее НА) добавляются все устройства аккаунта. diff --git a/custom_components/zont_ha/config_flow.py b/custom_components/zont_ha/config_flow.py index 7987e10..b3d1c78 100644 --- a/custom_components/zont_ha/config_flow.py +++ b/custom_components/zont_ha/config_flow.py @@ -1,3 +1,4 @@ +import base64 import logging import re from http import HTTPStatus @@ -6,20 +7,49 @@ from homeassistant import config_entries from homeassistant.core import HomeAssistant -from .const import DOMAIN +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from .const import DOMAIN, URL_GET_TOKEN from .core.exceptions import RequestAPIZONTError, InvalidMail +from .core.models_zont import ErrorZont, TokenZont from .core.zont import Zont _LOGGER = logging.getLogger(__name__) -async def validate_auth(hass: HomeAssistant, mail: str, token: str) -> None: +async def get_token( + hass: HomeAssistant, mail: str, login: str, password: str +) -> str: + session = async_get_clientsession(hass) + encoded = f'{login}:{password}'.encode("utf-8") + basic = base64.b64encode(encoded).decode() + headers = { + 'Authorization': f'Basic {basic}', + 'X-ZONT-Client': mail, + 'Content-Type': 'application/json' + } + response = await session.post( + url=URL_GET_TOKEN, + json={'client_name': 'Home Assistant'}, + headers=headers + ) + text = await response.text() + status_code = response.status + if status_code != HTTPStatus.OK: + error = ErrorZont.parse_raw(text) + hass.data['error'] = error.error_ui + raise RequestAPIZONTError(error) + data = TokenZont.parse_raw(text) + return data.token + + +async def validate_auth_token( + hass: HomeAssistant, mail: str, token: str +) -> None: """Валидация токена zont""" zont = Zont(hass, mail, token) result = await zont.get_update() - _LOGGER.debug(f'validate_auth: {result}') if result != HTTPStatus.OK: hass.data['error'] = zont.error raise RequestAPIZONTError @@ -37,24 +67,82 @@ def validate_mail(mail: str) -> None: class ZontConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): - VERSION = 1 + VERSION = 2 + data: dict = None async def async_step_user(self, user_input=None): errors: dict[str, str] = {} if user_input is not None: try: - _LOGGER.debug(user_input) validate_mail(user_input['mail']) - await validate_auth( + if not errors: + self.data = user_input + if user_input.get('get_token', False): + return await self.async_step_auth_pswd() + else: + return await self.async_step_auth_token() + except InvalidMail: + _LOGGER.error(f"{user_input['mail']} - неверный формат") + errors['base'] = 'invalid_mail' + except Exception as e: + _LOGGER.error(f'Что-то пошло не так, неизвестная ошибка. {e}') + errors["base"] = "unknown" + return self.async_show_form( + step_id="user", + data_schema=vol.Schema( + { + vol.Required(schema="name"): str, + vol.Required(schema="mail"): str, + vol.Optional(schema="get_token"): bool + } + ), + errors=errors + ) + + async def async_step_auth_pswd(self, user_input=None): + errors: dict[str, str] = {} + if user_input is not None: + try: + token = await get_token( + self.hass, + self.data['mail'], + user_input['login'], + user_input['password'] + ) + if not errors: + self.data['token'] = token + return await self.async_step_auth_token() + except RequestAPIZONTError: + _LOGGER.error(self.hass.data['error']) + errors['base'] = 'invalid_auth' + except Exception as e: + _LOGGER.error(f'Что-то пошло не так, неизвестная ошибка. {e}') + errors["base"] = "unknown" + return self.async_show_form( + step_id="auth_pswd", + data_schema=vol.Schema( + { + vol.Required("login"): str, + vol.Required("password"): str + } + ), + errors=errors + ) + + async def async_step_auth_token(self, user_input=None): + errors: dict[str, str] = {} + if user_input is not None: + try: + await validate_auth_token( self.hass, - user_input['mail'], user_input['token'] + self.data['mail'], + user_input['token'] ) + if not errors: + self.data.update(user_input) return self.async_create_entry( - title=user_input['name'], data=user_input + title=self.data['name'], data=self.data ) - except InvalidMail: - _LOGGER.error(f"{user_input['mail']} - неверный формат") - errors['base'] = 'invalid_mail' except RequestAPIZONTError: _LOGGER.error(self.hass.data['error']) errors['base'] = 'invalid_auth' @@ -62,12 +150,12 @@ async def async_step_user(self, user_input=None): _LOGGER.error(f'Что-то пошло не так, неизвестная ошибка. {e}') errors["base"] = "unknown" return self.async_show_form( - step_id="user", + step_id="auth_token", data_schema=vol.Schema( { - vol.Required("name"): str, - vol.Required("mail"): str, - vol.Required("token"): str + vol.Required( + schema="token", default=self.data.get('token', None) + ): str } ), errors=errors diff --git a/custom_components/zont_ha/const.py b/custom_components/zont_ha/const.py index 1ddfd53..0e798e7 100644 --- a/custom_components/zont_ha/const.py +++ b/custom_components/zont_ha/const.py @@ -5,9 +5,9 @@ DOMAIN = 'zont_ha' MANUFACTURER = 'MicroLine' -ZONT_API_URL = "https://lk.zont-online.ru/api/widget/v2/" +ZONT_API_URL = 'https://lk.zont-online.ru/api/widget/v2/' -URL_SEND_COMMAND_ZONT_OLD = "https://lk.zont-online.ru/api/send_z3k_command" +URL_SEND_COMMAND_ZONT_OLD = 'https://lk.zont-online.ru/api/send_z3k_command' URL_GET_DEVICES = 'https://lk.zont-online.ru/api/widget/v2/get_devices' URL_SET_TARGET_TEMP = 'https://lk.zont-online.ru/api/widget/v2/set_target_temp' diff --git a/custom_components/zont_ha/core/models_zont.py b/custom_components/zont_ha/core/models_zont.py index b845a26..9b6034a 100644 --- a/custom_components/zont_ha/core/models_zont.py +++ b/custom_components/zont_ha/core/models_zont.py @@ -101,6 +101,14 @@ class AccountZont(BaseModel): ok: bool +class TokenZont(BaseModel): + """Клас ответа получения токена""" + + token: str + token_id: str + ok: bool + + class ErrorZont(BaseModel): """Клас ответа об ошибке""" diff --git a/custom_components/zont_ha/core/zont.py b/custom_components/zont_ha/core/zont.py index a626fe9..0ef19ad 100644 --- a/custom_components/zont_ha/core/zont.py +++ b/custom_components/zont_ha/core/zont.py @@ -54,7 +54,7 @@ def __init__(self, hass: HomeAssistantType, mail: str, token: str): } self.mail = mail self.session = async_get_clientsession(hass) - _LOGGER.warning(f'Создан объект Zont') + _LOGGER.debug(f'Создан объект Zont') async def get_update(self): """Получаем обновление данных объекта Zont""" diff --git a/custom_components/zont_ha/manifest.json b/custom_components/zont_ha/manifest.json index 77a025e..ed40744 100644 --- a/custom_components/zont_ha/manifest.json +++ b/custom_components/zont_ha/manifest.json @@ -7,5 +7,5 @@ "integration_type": "hub", "dependencies": ["http", "zeroconf"], "requirements": [], - "version": "0.1.5" + "version": "0.2.0" } \ No newline at end of file diff --git a/custom_components/zont_ha/translations/en.json b/custom_components/zont_ha/translations/en.json index 3bf95b9..333769e 100644 --- a/custom_components/zont_ha/translations/en.json +++ b/custom_components/zont_ha/translations/en.json @@ -12,7 +12,20 @@ "data": { "name": "Название", "mail": "mail", - "token": "токен аккаунта" + "get_token": "Получить токен" + } + }, + "auth_pswd": { + "title": "Получить токен", + "data": { + "login": "Логин", + "password": "Пароль" + } + }, + "auth_token": { + "title": "Авторизация в ZONT", + "data": { + "token": "Токен" } } }